Search code examples
javaspringspring-bootintegration-testingwiremock

Multiple Application Contexts in Spring JUnit test with WireMock


I have the following scenario:

@Transactional
@SpringBootTest
@ActiveProfiles("test")
@AutoConfigureMockMvc
@AutoConfigureWireMock(port = 0)
public abstract class IntegrationTest {
}

public class Test1 extends IntegrationTest {
  // Tests that use WireMock
}

@ActiveProfiles("specific-case-test") // This causes another Application Context to be created.
public class Test2 extends IntegrationTest {
  // Tests that use WireMock
}

public class Test3 extends IntegrationTest {
  // Tests that use WireMock
}

Tests run successfully in all these scenarios:

  • Run the tests individually
  • In the order: Test1, Test3, Test2
  • In the order: Test3, Test1, Test2
  • In the order: Test2, Test3, Test1
  • In the order: Test2, Test1, Test3

The last test to run fails in all these scenarios:

  • In the order: Test1, Test2, Test3
  • In the order: Test3, Test2, Test1

I already investigated the problem and it is related to the Spring Application Context and WireMock.

What is going on? Let's consider that the tests run in that order: Test1, Test2, Test3.

When Test1 runs, an Application Context (AC1) is created and a WireMock server (WM1) is set up, let's say, on port 1. Port 1 is set to AC1 (wiremock.server.port) and WM1 is attached to the test thread. All test passes.

When Test2 runs, another Application Context (AC2) is created and a new WireMock server (WM2) is set up, let's say, on port 2. Port 2 is set to AC2 (wiremock.server.port) and WM2 is attached to the test thread, replacing WM1. All test passes.

When Test3 runs, it reuses AC1 and this causes the tests to fail with the message: 404 Not Found: [No response could be served as there are no stub mappings in this WireMock instance.]. The application state is that wiremock.server.port is 1 (comes from AC1) and WM2 is attached to the test thread. Because of that, stubbing happens against WM2, but the application rest calls are going to WM1, which is listening on port 1.

I already tried to clean the Application Context adding @DirtiesContext to Test2, so it would force Spring to load the third AC, but it doesn't work. However, if I add @DirtiesContext to Test1 or @DirtiesContext(classMode = BEFORE_CLASS) to Test3 it works. I don't want this solution because I have other tests and there is no guarantee in which order the tests will run, so if I add it to Test3, then later the execution order will change and another test will fail. I would like a real solution.

Any ideas?


Solution

  • Not sure if you have found a solution to this but here is how I resolved this.

    When the spring cached test contexts are reused the wiremock port changes back to that contexts port but seems to miss some step of configuring the WireMock classes default server config. We had to call WireMock.configureFor(port) in a @Before or @BeforeEach method with the port of the currently running context. This meant that when we did stubFor method calls then the right WireMock port was hit and the server got configured with our stubs correctly, make sure you do this configuring before any resets too.

    @Autowired
    private Environment environment;
    
    private String getWiremockServerPort() {
        // Get the auto configured port property from the current Spring contexts environment
        return environment.getProperty("wiremock.server.port");
    }
    
    @BeforeEach
    private void configureWireMockPortToMatchEnvironmentContext() {
        int contextEnvironmentPort = Integer.parseInt(getWiremockServerPort());
        configureFor(contextEnvironmentPort);
    }