ArchUnit icon indicating copy to clipboard operation
ArchUnit copied to clipboard

Capturing calls to org.junit.jupiter.api.assertThrows<MyCustomException>

Open vincent-fuchs opened this issue 1 year ago • 2 comments

hi,

in my Kotlin code base, I have quite a lot of calls to org.junit.jupiter.api.assertThrows<MyCustomException> , and depending on the package, I want to block it or not.

I am a bit stuck on how to capture the exception type in an ArchUnit condition...

so far, I am able to identify the places where assertThrows is called, but what other predicate can I use to filter on the exception type ?

        val callOfAssertThrowsMyCustomException = callMethodWhere(
                target(name("assertThrows"))
         //then what ?
        )

is it even possible ? I have a doubt because when I try a different approach in debug mode, I am able to find the codeUnit I am interested in, but what I see is not reassuring :

image

we see that the exception "real" class doesn't seem to be available, it's just a Class.

If a more precise type information is not available to ArchUnit, it's going to be difficult, right ?

vincent-fuchs avatar Sep 08 '23 20:09 vincent-fuchs

You're right; the generic type of org.junit.jupiter.api.Assertions.assertThrows does not show up in the byte code. ArchUnit therefore cannot directly distinguish assertThrows<MyCustomException> from assertThrows<AnyOtherException>.

hankem avatar Sep 09 '23 02:09 hankem

I wonder whether testing for class objects referenced from the same test method (it's not possible to attribute them directly to a method call) could be a useful workaround – even though this will have false positives and negatives.

This:

ArchRule rule = noClasses().should().callMethodWhere(
        target(name("assertThrows")).and(describe("", call ->
                call.getOwner().getReferencedClassObjects().stream()
                        .map(ReferencedClassObject::getRawType)
                        .anyMatch(javaClass -> javaClass.isEquivalentTo(NullPointerException.class))
        ))
);

is able to distinguish those:

@Test void test1() {
    assertThrows(NullPointerException.class, () -> requireNonNull(null));
}

@Test void test2() {
    assertThrows(IllegalArgumentException.class, () -> checkArgument(false));
}

hankem avatar Sep 09 '23 02:09 hankem