Stateful Behaviour

Most web services tend to have some state, which changes as you and others interact with it. So it’s pretty useful to be able to simulate this when you’ve swapped a real service for a test double.

Scenarios #

WireMock supports state via the notion of scenarios. A scenario is essentially a state machine whose states can be arbitrarily assigned. Its starting state is always Scenario.STARTED. Stub mappings can be configured to match on scenario state, such that stub A can be returned initially, then stub B once the next scenario state has been triggered.

For example, suppose we’re writing a to-do list application consisting of a rich client of some kind talking to a REST service. We want to test that our UI can read the to-do list, add an item and refresh itself, showing the updated list.

In Java this could be set up like this:

@Test
public void toDoListScenario() {
    stubFor(get(urlEqualTo("/todo/items")).inScenario("To do list")
            .whenScenarioStateIs(STARTED)
            .willReturn(aResponse()
                    .withBody("<items>" +
                            "   <item>Buy milk</item>" +
                            "</items>")));

    stubFor(post(urlEqualTo("/todo/items")).inScenario("To do list")
            .whenScenarioStateIs(STARTED)
            .withRequestBody(containing("Cancel newspaper subscription"))
            .willReturn(aResponse().withStatus(201))
            .willSetStateTo("Cancel newspaper item added"));

    stubFor(get(urlEqualTo("/todo/items")).inScenario("To do list")
            .whenScenarioStateIs("Cancel newspaper item added")
            .willReturn(aResponse()
                    .withBody("<items>" +
                            "   <item>Buy milk</item>" +
                            "   <item>Cancel newspaper subscription</item>" +
                            "</items>")));

    WireMockResponse response = testClient.get("/todo/items");
    assertThat(response.content(), containsString("Buy milk"));
    assertThat(response.content(), not(containsString("Cancel newspaper subscription")));

    response = testClient.postWithBody("/todo/items", "Cancel newspaper subscription", "text/plain", "UTF-8");
    assertThat(response.statusCode(), is(201));

    response = testClient.get("/todo/items");
    assertThat(response.content(), containsString("Buy milk"));
    assertThat(response.content(), containsString("Cancel newspaper subscription"));
}

The JSON equivalent for the above three stubs is:

{
    "mappings": [
        {
            "scenarioName": "To do list",
            "requiredScenarioState": "Started",
            "request": {
                "method": "GET",
                "url": "/todo/items"
            },
            "response": {
                "status": 200,
                "body": "<items><item>Buy milk</item></items>"
            }
        },
        {
            "scenarioName": "To do list",
            "requiredScenarioState": "Started",
            "newScenarioState": "Cancel newspaper item added",
            "request": {
                "method": "POST",
                "url": "/todo/items",
                "bodyPatterns": [
                    { "contains": "Cancel newspaper subscription" }
                ]
            },
            "response": {
                "status": 201
            }
        },
        {
            "scenarioName": "To do list",
            "requiredScenarioState": "Cancel newspaper item added",
            "request": {
                "method": "GET",
                "url": "/todo/items"
            },
            "response": {
                "status": 200,
                "body": "<items><item>Buy milk</item><item>Cancel newspaper subscription</item></items>"
            }
        }
    ]
}

Getting scenario state #

The names, current state and possible states of all scenarios can be fetched.

Java:

List<Scenario> allScenarios = getAllScenarios();

JSON:

GET /__admin/scenarios
{
  "scenarios" : [ {
    "id" : "my_scenario",
    "name" : "my_scenario",
    "state" : "Started",
    "possibleStates" : [ "Started", "state_2", "state_3" ]
  } ]
}

Resetting scenarios #

The state of all configured scenarios can be reset back to Scenario.START either by calling

Java:

WireMock.resetAllScenarios()

To do the equivalent via the HTTP API, send an empty POST request to /__admin/scenarios/reset.

Resetting a single scenario #

You can reset the state of an individual scenario.

Java:

WireMock.resetScenario("my_scenario");

The do the equivalent via the HTTP API, send an empty PUT to /__admin/scenarios/my_scenario/state.

Setting the state of an individual scenario #

You can also set the state of an individual scenario to a specific value.

Java:

WireMock.setScenarioState("my_scenario", "state_2");

HTTP:

PUT /__admin/scenarios/my_scenario/state
{
    "state": "state_2"
}