Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JUNIT 5: Inject spring components to custom TestTemplateInvocationContextProvider

Tags:

spring

junit5

Is there a way in JUnit Jupiter (JUnit 5) that makes it possible to inject Spring components into a TestTemplateInvocationContextProvider?

like image 529
gkatzioura Avatar asked Mar 06 '23 06:03

gkatzioura


1 Answers

Yes, if you register your TestTemplateInvocationContextProvider as a bean in the Spring ApplicationContext loaded for your test class, you can then have the provider @Autowired into a field and registered as a JUnit Jupiter extension using @RegisterExtension. The trick is that you'll need to use the per-class test instance lifecycle mode in order for the provider to be registered early enough for JUnit Jupiter to use it.

The following is a modified version of TestTemplateDemo from the JUnit 5 User Guide.

The tests pass "as is", but you can remove the // from the @Bean declaration for the baz bean to see a test fail.

@SpringJUnitConfig
@TestInstance(Lifecycle.PER_CLASS)
class TestTemplateDemo {

    @Autowired
    @RegisterExtension
    TestTemplateInvocationContextProvider testTemplateInvocationContextProvider;

    @TestTemplate
    void testTemplate(String parameter) {
        assertTrue("foo".equals(parameter) || "bar".equals(parameter));
    }

    @Configuration
    static class Config {

        @Bean
        String foo() {
            return "foo";
        }

        @Bean
        String bar() {
            return "bar";
        }

        // @Bean
        String baz() {
            return "baz";
        }

        @Bean
        TestTemplateInvocationContextProvider myTestTemplateInvocationContextProvider(
                List<String> parameters) {

            return new MyTestTemplateInvocationContextProvider(parameters);
        }
    }

    public static class MyTestTemplateInvocationContextProvider
            implements TestTemplateInvocationContextProvider {

        private final List<String> parameters;

        public MyTestTemplateInvocationContextProvider(List<String> parameters) {
            this.parameters = parameters;
        }

        @Override
        public boolean supportsTestTemplate(ExtensionContext context) {
            return true;
        }

        @Override
        public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
                ExtensionContext context) {

            return this.parameters.stream().map(p -> invocationContext(p));
        }

        private TestTemplateInvocationContext invocationContext(String parameter) {
            return new TestTemplateInvocationContext() {

                @Override
                public String getDisplayName(int invocationIndex) {
                    return parameter;
                }

                @Override
                public List<Extension> getAdditionalExtensions() {
                    return Collections.singletonList(new ParameterResolver() {

                        @Override
                        public boolean supportsParameter(
                                ParameterContext parameterContext,
                                ExtensionContext extensionContext) {
                            return parameterContext.getParameter().getType().equals(
                                    String.class);
                        }

                        @Override
                        public Object resolveParameter(ParameterContext parameterContext,
                                ExtensionContext extensionContext) {
                            return parameter;
                        }
                    });
                }
            };
        }
    }

}
like image 101
Sam Brannen Avatar answered Apr 30 '23 08:04

Sam Brannen