Search code examples
javatestingjunit5

BeforeAll / AfterAll callbacks of junit5 extension are executed for each nested test class. Is this expected?


I have a test class with multiple nested test classes inside. The outer test class uses an extension that implements BeforeAllCallback and AfterAllCallback. The methods of these interfaces are called for each nested class when executing the outer test class. Is this expected behaviour? I could not find any documentation specifying this explicitly.

The outer test class has @BeforeAll and @AfterAll methods as well. These are executed once when executing the outer test class. I kind of expected extensions to behave the same.

If this behaviour is indeed expected, is there a way to suppress it?

Here is a minimal code example. Non-custom extensions e.g. SpringExtension show the same behaviour however:

@ExtendWith(MyExtension.class)
public class SomeTest {

    @BeforeAll
    static void create() {
        System.out.println("Call beforeAll of test class");
    }

    @AfterAll
    static void destroy() {
        System.out.println("Call afterAll of test class");
    }

    @Nested
    class InnerTest1 {
        @Test
        void testingA() {
            System.out.println("Test 1A");
        }

        @Test
        void testingB() {
            System.out.println("Test 1B");
        }
    }

    @Nested
    class InnerTest2 {
        @Test
        void testingA() {
            System.out.println("Test 2A");
        }

        @Test
        void testingB() {
            System.out.println("Test 2B");
        }
    }
}
public class MyExtension implements BeforeAllCallback, AfterAllCallback {

    public MysqlMockExtension() {
    }

    @Override
    public void beforeAll(final ExtensionContext extensionContext) throws Exception {
        System.out.println("Call beforeAll of extension");
    }

    @Override
    public void afterAll(final ExtensionContext extensionContext) throws Exception {
        System.out.println("Call afterAll of extension");
    }
}

Leads to output:

Call beforeAll of extension
Call beforeAll of test class

Call beforeAll of extension
Test 2A
Test 2B
Call afteeAll of extension

Call beforeAll of extension
Test 1A
Test 1B
Call afteeAll of extension

Call afterAll of test class
Call afteeAll of extension

Solution

  • The reason for the observed behaviour is the fact that all extensions you add to a test class in Jupiter is inherited by all its children, be it test methods, or nested test classes. What you could do is check if the class the extension is used on is a top-level class:

    class MyExtension implements BeforeAllCallback {
    
        @Override
        public void beforeAll(final ExtensionContext extensionContext) throws Exception {
            if (extensionContext.getTestClass().isPresent()) {
                Class<?> currentClass = extensionContext.getTestClass().get();
                if (isNestedClass(currentClass)) {
                    return;
                }
            }
            System.out.println("Call beforeAll of extension");
        }
    
        private boolean isNestedClass(Class<?> currentClass) {
            return !ModifierSupport.isStatic(currentClass) && currentClass.isMemberClass();
        }
    
    }
    

    As far as I know Jupiter has no mechanism to restrict an extension to the location of the @ExtendWith annotation. You might open an issue to request this feature.