1. Overview
1.概述
This quick tutorial will show how we can test a stateful HTTP-based API with WireMock.
这个快速教程将展示我们如何用WireMock测试一个有状态的基于HTTP的API。
To get started with the library, have a look at our Introduction to WireMock tutorial first.
要开始使用该库,请先看看我们的WireMock入门教程。
2. Maven Dependencies
2.Maven的依赖性
In order to be able to take advantage of the WireMock library, we need to include the following dependency in the POM:
为了能够利用WireMock库,我们需要在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
3.我们要模拟的API实例
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.
Wiremock中场景的概念是帮助模拟REST API的不同状态。这使我们能够创建测试,在测试中,我们使用的API会根据它所处的状态而表现出不同的行为。
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.
为了说明这一点,我们将看一个实际的例子:一个 “Java提示 “服务,每当我们请求它的/java-tip端点时,它就会给我们一个关于Java的不同提示。
If we ask for a tip, we’d get one back in text/plain:
如果我们要求提供小费,我们会得到一个text/plain的回报。
"use composition rather than inheritance"
If we called it again, we’d get a different tip.
如果我们再打电话,就会得到不同的提示。
4. Creating the Scenario States
4.创建场景状态
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:
我们需要让WireMock为“/java-tip”/em>端点创建存根。这些存根将分别返回与模拟API的3种状态之一相对应的特定文本。
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.
在上面的类中,我们使用WireMock的JUnit规则类WireMockRule。这在JUnit测试运行时设置了WireMock服务器。
We then use WireMock’s stubFor method to create the stubs that we’ll use later.
然后我们使用WireMock的stubFor方法来创建我们以后将使用的存根。
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.
任何场景的初始状态都是Scenario.STARTED。因此,我们创建一个存根,当状态为Scenario.STARTED.时使用,这样就把状态移到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:
我们还添加了存根来从SECOND_STATE移动到THIRD_STATE,最后从THIRD_STATE返回到Scenario.STARTED.所以如果我们继续调用 /java-tip端点,状态会有如下变化。
Scenario.STARTED -> SECOND_STATE -> THIRD_STATE -> Scenario.STARTED
Scenario.STARTED -> SECOND_STATE -> THIRD_STATE -> Scenario.STARTED
5. Using the Scenario
5.使用该方案
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:
为了使用WireMock场景,我们只需对/java-tip端点进行重复调用。所以我们需要修改我们的测试类,如下。
@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.
nextTip()方法调用/java-tip端点,然后以String形式返回响应。因此,我们在每个 assertEquals()调用中使用它,以检查这些调用是否确实让场景在不同的状态下循环。
6. Conclusion
6.结语
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.
在这篇文章中,我们看到了如何使用WireMock Scenarios来模拟一个API,这个API会根据它所处的状态来改变其响应。
As always, all the code used in this tutorial is available over on GitHub.
一如既往,本教程中使用的所有代码都可以在GitHub上找到。