Steve K
Steve K

Reputation: 2164

How to get callback in Junit 5 *before* Spring test context is loaded?

I'm using a Junit 5 extension to start up a Wiremock server, before any tests run. However, one of the beans in the Spring context makes a remote call as part of its initialization, which I can't change, and that call results in a ConnectionException because the Wiremock server hasn't started yet.

How do I configure my JUnit 5 test to get a callback before Spring loads the text context?

My JUnit 5 extension looks like this:

public class MyWiremockExtension implements BeforeAllCallback, AfterAllCallback {

  private final WireMockServer wireMock = new WireMockServer(...);
  
  @Override
  public void beforeAll(ExtensionContext extensionContext) throws Exception {
    wireMock.start();
  }

  @Override
  public void afterAll(ExtensionContext extensionContext) throws Exception {
    wireMock.stop();
  }
}

The Spring Bean config is buried deep in upstream code that my OkHttpClient bean depends on, but it looks something like this:

@Configuration
public class OkHttpClientConfiguration {

  @Bean
  OkHttpClient okHttpClient(...) {
    OkHttpClient okHttpClient = new OkHttpClient.Builder()...build();
    // wrap the okHttpClient in OAuth handling code which eagerly fetches a token
  }
}

My test looks like this:

@SpringBootTest(properties = {...})
@ExtendWith(MyWiremockExtension.class)
class MyTest {
...
}

The closest answer I've found so far is How to register Spring Context Events for current test ApplicationContext at runtime, but that doesn't provide a callback method for before the test context loads.

My best guess about how to do this is either:

  1. create my own ContextCustomizerFactory, or
  2. extend SpringBootTestContextBootstrapper, override buildTestContext() to start wiremock before calling super.buildTestContext(), then @BootstrapWith my class instead of Spring Boot's, though I'm not sure which callback I'd use to stop the wiremock server.

Upvotes: 9

Views: 2195

Answers (2)

Philippe Marschall
Philippe Marschall

Reputation: 4604

Here is what worked for me:

  • Implement it with the Spring TestContext framework, this also makes it work with TestNG
  • implement TestExecutionListener
  • make the test execution listener implement Ordered
  • implement getOrder and return a value less than 2000 (order of DependencyInjectionTestExecutionListener)

Sample code https://github.com/marschall/spring-test-scope/blob/master/src/main/java/com/github/marschall/spring/test/scope/TestScopeTestExecutionListener.java

Upvotes: 0

Bernd Farka
Bernd Farka

Reputation: 512

what worked for me in a similar case:

created a own Annotation and specify the extensions there

@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(
    {
        MyWiremockExtension.class,
        SpringExtension.class,
    }
)
public @interface WireMockSpringTest {

this preserves the order in my case

Upvotes: 1

Related Questions