TecNimbus

Let’s build better software—and a more balanced life—together.

, , ,

API Testing Made Easy: Mock External Services with WireMock and Spring Boot

🎯 Use Case

We have an API that integrates with an external service. With ongoing development changes, we need to test our API as an isolated component whenever necessary.

⚔️ Challenge

Testing our API component against the actual external API isn’t always practical. There are several challenges that can arise—such as the external service being temporarily unavailable, rate limits that restrict frequent calls, or even costs associated with hitting production-like environments. Despite these limitations, we still need the flexibility to test our component reliably and independently, without relying on the external API’s availability or behaviour.

🚀 Approach

So, how can we overcome this dependency? One effective solution is to mock the external API during testing.

As an example, let’s take our petstore-api-demo app. When we invoke it, it internally makes a call to an external remote Pet Store service.

Implementation approach

To implement this setup, we can use WireMockServer, which allows us to mock external API calls without actually reaching out to the real external service. This is particularly useful for isolated and reliable testing.

We’ll use @SpringBootTest to structure our integration test, and we’ll start the WireMock server as part of the test setup. This way, the test can simulate the external API responses while keeping everything self-contained and repeatable.

✅ 1. Add WireMock Dependency
testImplementation 'org.wiremock:wiremock-standalone:3.3.1'
✅ 2. Create an application-test.yaml for Testing

When running the integration tests, we plan to use a dedicated test application YAML configuration file instead of the real integration configuration. This ensures that our tests are isolated and use mock endpoints rather than actual external services.

external:
  pets-tore:
    url: http://localhost:9561
✅ 3. Create @SpringBootTest with WireMock Server
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@DisplayName("Pet API Integration Test with WireMock")
public class PetControllerIntegrationTest {

    private WireMockServer wireMockServer;

    @LocalServerPort
    private int port;

    private final TestRestTemplate restTemplate = new TestRestTemplate();

    @BeforeAll
    void startWireMock() {
        wireMockServer = new WireMockServer(9561); // Port must match test YAML
        wireMockServer.start();

        configureFor("localhost", 9561);
        stubFor(get(urlEqualTo("/pet/1"))
                .willReturn(aResponse()
                        .withHeader("Content-Type", "application/json")
                        .withBody("""
                                    {
                                      "id": 1,
                                      "category": {
                                        "id": 1,
                                        "name": "string"
                                      },
                                      "name": "doggie",
                                      "photoUrls": [
                                        "string"
                                      ],
                                      "tags": [
                                        {
                                          "id": 1,
                                          "name": "string"
                                        }
                                      ],
                                      "status": "available"
                                    }
                        """)
                        .withStatus(200)));
    }

    @AfterAll
    void stopWireMock() {
        wireMockServer.stop();
    }

    @Test
    void testGetPetById_shouldReturnMockedPet() {
        String url = "http://localhost:" + port + "/api/v1/pet/1";

        ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(response.getBody()).contains("doggie");
    }
}
  • We use the @ActiveProfiles(“test”) annotation to activate the application-test.yaml configuration during testing. This ensures that the test environment uses the appropriate mock settings and avoids calling real external services.
  • @BeforeAll
    • Start on port 9561
    • Launch WireMock
    • Configure WireMock to accept requests
    • Set up a stub/mock for GET /api/v1/pet/1
    • When your app calls GET http://localhost:9561/api/v1/pet/1, WireMock will return a mocked 200 OK response with the given JSON.
  • @AfterAll : Stops the WireMock server that was started in @BeforeAll.
  • @Test
    • Sends a real HTTP GET request to your running Spring Boot app (/api/v1/pet/1)
    • Your app internally makes a mocked call to WireMock
    • The test verifies:
      • The response status is 200 OK
      • The body contains "doggie" (from the WireMock stub)