Skip to content

Stateful Behaviour

WireMock Cloud

Build fully stateful mock API behaviour using templates in WireMock Cloud
Read the docs >

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.

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>"
}
}
]
}

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" ]
} ]
}

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.

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

Section titled “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"
}