Generic Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Overview

This quick tutorial will show how we can test a stateful HTTP-based API with WireMock.

To get started with the library, have a look at our Introduction to WireMock tutorial first.

2. Maven Dependencies

In order to be able to take advantage of the WireMock library, we need to include the following dependency in the POM:

<dependency>
    <groupId>com.github.tomakehurst</groupId>
    <artifactId>wiremock</artifactId>
    <version>2.21.0</version>
    <scope>test</scope>
</dependency>

3. The Example API We Want to Mock

The concept of Scenarios in Wiremock is to help simulate the different states of a REST API. This enables us to create tests in which the API that we’re using behaves differently depending on the state it’s in.

To illustrate this, we’ll have a look at a practical example: a “Java Tip” service which gives us a different tip about Java whenever we request its /java-tip endpoint.

If we ask for a tip, we’d get one back in text/plain:

"use composition rather than inheritance"

If we called it again, we’d get a different tip.

4. Creating the Scenario States

We need to get WireMock to create stubs for the “/java-tip” endpoint. The stubs will each return a certain text that corresponds to one of the 3 states of the mock API:

public class WireMockScenarioExampleIntegrationTest {
    private static final String THIRD_STATE = "third";
    private static final String SECOND_STATE = "second";
    private static final String TIP_01 = "finally block is not called when System.exit()" 
      + " is called in the try block";
    private static final String TIP_02 = "keep your code clean";
    private static final String TIP_03 = "use composition rather than inheritance";
    private static final String TEXT_PLAIN = "text/plain";
    
    static int port = 9999;
    
    @Rule
    public WireMockRule wireMockRule = new WireMockRule(port);    

    @Test
    public void changeStateOnEachCallTest() throws IOException {
        createWireMockStub(Scenario.STARTED, SECOND_STATE, TIP_01);
        createWireMockStub(SECOND_STATE, THIRD_STATE, TIP_02);
        createWireMockStub(THIRD_STATE, Scenario.STARTED, TIP_03);
        
    }

    private void createWireMockStub(String currentState, String nextState, String responseBody) {
        stubFor(get(urlEqualTo("/java-tip"))
          .inScenario("java tips")
          .whenScenarioStateIs(currentState)
          .willSetStateTo(nextState)
          .willReturn(aResponse()
            .withStatus(200)
            .withHeader("Content-Type", TEXT_PLAIN)
            .withBody(responseBody)));
    }

}

In the above class, we use WireMock’s JUnit rule class WireMockRule. This sets up the WireMock server when the JUnit test is run.

We then use WireMock’s stubFor method to create the stubs that we’ll use later.

The key methods used when creating the stubs are:

  • whenScenarioStateIs: defines which state the scenario needs to be in for WireMock to use this stub
  • willSetStateTo: gives the value that WireMock sets the state to after this stub has been used

The initial state of any scenario is Scenario.STARTED. So, we create a stub which is used when the state is Scenario.STARTED. This moves the state on to SECOND_STATE.

We also add stubs to move from SECOND_STATE to THIRD_STATE and finally from THIRD_STATE back to Scenario.STARTED. So if we keep calling the /java-tip endpoint the state changes as follows:

Scenario.STARTED -> SECOND_STATE -> THIRD_STATE -> Scenario.STARTED

5. Using the Scenario

In order to use the WireMock Scenario, we simply make repeated calls to the /java-tip endpoint. So we need to modify our test class as follows:

    @Test
    public void changeStateOnEachCallTest() throws IOException {
        createWireMockStub(Scenario.STARTED, SECOND_STATE, TIP_01);
        createWireMockStub(SECOND_STATE, THIRD_STATE, TIP_02);
        createWireMockStub(THIRD_STATE, Scenario.STARTED, TIP_03);

        assertEquals(TIP_01, nextTip());
        assertEquals(TIP_02, nextTip());
        assertEquals(TIP_03, nextTip());
        assertEquals(TIP_01, nextTip());        
    }

    private String nextTip() throws ClientProtocolException, IOException {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet request = new HttpGet(String.format("http://localhost:%s/java-tip", port));
        HttpResponse httpResponse = httpClient.execute(request);
        return firstLineOfResponse(httpResponse);
    }

    private static String firstLineOfResponse(HttpResponse httpResponse) throws IOException {
        try (BufferedReader reader = new BufferedReader(
          new InputStreamReader(httpResponse.getEntity().getContent()))) {
            return reader.readLine();
        }
    }

The nextTip() method calls the /java-tip endpoint and then returns the response as a String. So we use that in each assertEquals() call to check that the calls do indeed get the scenario to cycle around the different states.

6. Conclusion

In this article, we saw how to use WireMock Scenarios in order to mock an API which changes its response depending on the state it is in.

As always, all the code used in this tutorial is available over on GitHub.

Generic bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

Leave a Reply

avatar
  Subscribe  
Notify of