javajunitjunit5junit5-extension-model

Check that JUnit Extension throws specific Exception


Suppose I develop an extension which disallows test method names to start with an uppercase character.

public class DisallowUppercaseLetterAtBeginning implements BeforeEachCallback {

    @Override
    public void beforeEach(ExtensionContext context) {
        char c = context.getRequiredTestMethod().getName().charAt(0);
        if (Character.isUpperCase(c)) {
            throw new RuntimeException("test method names should start with lowercase.");
        }
    }
}

Now I want to test that my extension works as expected.

@ExtendWith(DisallowUppercaseLetterAtBeginning.class)
class MyTest {

    @Test
    void validTest() {
    }

    @Test
    void TestShouldNotBeCalled() {
        fail("test should have failed before");
    }
}

How can I write a test to verify that the attempt to execute the second method throws a RuntimeException with a specific message?


Solution

  • After trying the solutions in the answers and the question linked in the comments, I ended up with a solution using the JUnit Platform Launcher.

    class DisallowUppercaseLetterAtBeginningTest {
    
        @Test
        void should_succeed_if_method_name_starts_with_lower_case() {
            TestExecutionSummary summary = runTestMethod(MyTest.class, "validTest");
    
            assertThat(summary.getTestsSucceededCount()).isEqualTo(1);
        }
    
        @Test
        void should_fail_if_method_name_starts_with_upper_case() {
            TestExecutionSummary summary = runTestMethod(MyTest.class, "InvalidTest");
    
            assertThat(summary.getTestsFailedCount()).isEqualTo(1);
            assertThat(summary.getFailures().get(0).getException())
                    .isInstanceOf(RuntimeException.class)
                    .hasMessage("test method names should start with lowercase.");
        }
    
        private TestExecutionSummary runTestMethod(Class<?> testClass, String methodName) {
            SummaryGeneratingListener listener = new SummaryGeneratingListener();
    
            LauncherDiscoveryRequest request = request().selectors(selectMethod(testClass, methodName)).build();
            LauncherFactory.create().execute(request, listener);
    
            return listener.getSummary();
        }
    
        @ExtendWith(DisallowUppercaseLetterAtBeginning.class)
        static class MyTest {
    
            @Test
            void validTest() {
            }
    
            @Test
            void InvalidTest() {
                fail("test should have failed before");
            }
        }
    }
    

    JUnit itself will not run MyTest because it is an inner class without @Nested. So there are no failing tests during the build process.

    Update

    JUnit itself will not run MyTest because it is an inner class without @Nested. So there are no failing tests during the build process.

    This is not completly correct. JUnit itself would also run MyTest, e.g. if "Run All Tests" is started within the IDE or within a Gradle build.

    The reason why MyTest was not executed is because I used Maven and I tested it with mvn test. Maven uses the Maven Surefire Plugin to execute tests. This plugin has a default configuration which excludes all nested classes like MyTest.

    See also this answer about "Run tests from inner classes via Maven" and the linked issues in the comments.