SimonT
SimonT

Reputation: 165

Customize Test Application Context

I've been desperately trying to build an extension that requires information from both the JUnit5 extension model and the Spring-Boot Test framework. Specifically, I'd like to hook into the ApplicationContext creation process using a ApplicationContextInitializer and a custom annotation:

@Retention(RUNTIME)
@Target(TYPE)
@ContextConfiguration(initializers = CustomContextInitializer.class)
public @interface CustomAnnotation {
    String someOption();
}

The test then looks like this:

@SpringBootTest
@CustomAnnotation(someOption = "Hello There")
public class SomeTest {
    ...
}

Now, how do I access the CustomAnnotation instance of the test class from within my CustomContextInitializer?

class CustomContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {

        // How to access the Test Class here?

        CustomAnnotation annotation = <Test Class>.getAnnotation(CustomAnnotation.class);
        System.out.println(annotation.someOption());
    }
}

Is it possible to somehow access the JUnit5 ExtensionContext during the creation of the ApplicationContext? It doesn't have to be from within a ApplicationContextInitializer. I just need a hook that is executed early enough so that I can inject some dynamically generated properties before the whole bean instantiation process actually starts.

Upvotes: 1

Views: 1687

Answers (2)

Alex
Alex

Reputation: 866

You can implement your own TestExecutionListener and use it to access annotation you mentioned

@Retention(RUNTIME)
@Target(ElementType.TYPE)
@TestExecutionListeners(listeners = CustomTestExecutionListener.class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
@interface CustomAnnotation {
    String someOption();
}

static class CustomTestExecutionListener implements TestExecutionListener {
    @Override
    public void beforeTestClass(TestContext testContext) throws Exception {
       final CustomAnnotation annotation = testContext.getTestClass().getAnnotation(CustomAnnotation.class);
       System.out.println(annotation.someOption());
    }
}

Upvotes: 0

Arho Huttunen
Arho Huttunen

Reputation: 1131

Take a look at @DynamicPropertySource for injecting properties before the bean initialization. You can then use @RegisterExtension to register a custom extension that reads the annotation properties and makes them available through some method:

@CustomAnnotation(someOption = "Hello There")
public class SomeTest {
    @RegisterExtension
    static CustomExtension extension = new CustomExtension();

    @DynamicPropertySource
    static void registerProperties(DynamicPropertyRegistry registry) {
        registry.add("property.you.need", 
          () -> customExtension.getProperty());
    }
}

public class CustomExtension implements BeforeAllCallback {
    private String property;

    public String getProperty() {
        return property;
    }

    @Override
    public void beforeAll(ExtensionContext context) throws Exception {
        CustomAnnotation annotation = context.getRequiredTestClass()
            .getAnnotation(CustomAnnotation.class);
        property = annotation.someOption();
    }
}

I know it doesn’t answer the question about hooking JUnit 5 with Spring initialization mechanism, but if dynamic properties is all you need, this solves exactly that.

Upvotes: 1

Related Questions