上一篇中介绍了SpringBoot已经提供了集成测试功能,即:如果使用WebEnvironment.RANDOM_PORT
和TestRestTemplate
,SpringBoot会把应用在一个随机端口真正启起来,然后在客户端发送了一个请求。
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class RandomPortExampleTests {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void exampleTest() {
String body = this.restTemplate.getForObject("/", String.class);
assertThat(body).isEqualTo("Hello World");
}
}
这样就是一个完全的集成测试。这也是和SpringMVC测试的主要区别。MVC测试不会把应用真正启动起来。应用收到客户端请求之后,应用内部如果有数据库操作,应用会真正操作数据库,所以可以在集成测试时使用内存数据库,以加快速度。
另一方面,如果应用内部有和其他系统或服务的API交互,应用也会真实发送出去。这时候我们就需要一个MockServer根据需要返回我们指定的结果,达到测试的目的。
一般工程中采用两种方式:
自己写一个MockServer,部署到某个地方,请求和响应都自己实现,来模拟下游系统。
使用第三方库,这样的库非常多,如WireMock,Mock Server,Raml,郑晔也写过一个Moco。
这介绍一下WireMock。
WireMock可以单独运行,可以在官网下到一个独立的可运行jar包,直接java -jar wiremock-standalone-2.6.0.jar
运行即可。
默认会运行在8080端口,通过localhost:8080/__admin(如果运行在本地,如果是远端替换成ip即可)。可以查看目前已经配置的请求和响应。
{
"mappings": [
{
"id": "20452ea9-5f9c-4b4c-87b6-8802abf358b6",
"request": {
"url": "/hello",
"method": "GET",
"headers": {
"x-auth-token": {
"contains": "123"
}
}
},
"response": {
"status": 200,
"body": "hello\n"
},
"uuid": "20452ea9-5f9c-4b4c-87b6-8802abf358b6"
},
{
"id": "5e69ff41-bcb0-43be-ba23-d99f818115e4",
"request": {
"url": "/more",
"method": "GET",
},
"response": {
"status": 200,
"body": "More content\n"
},
"uuid": "5e69ff41-bcb0-43be-ba23-d99f818115e4"
}
],
"meta": {
"total": 2
}
}
有两种方式可以配置请求响应:
通过代码控制,稍后介绍。
通过文件配置,WireMock启动后默认在当前目录创建两个文件,__files和__mappings,__files可以用来放置测试的响应,如返回的json或字符串。__mappings可以放置请求响应配置。
放置如下json文件在__mappings目录中,就可以添加一个请求响应。名称随意,可以添加任意多个文件。
{
"request": {
"method": "GET",
"url": "/hello",
"headers": {
"x-auth-token": {
"contains": "123"
}
}
},
"response": {
"status": 200,
"body": "Hell World\n"
}
}
这里指定一个header,客户端在发送请求时必须包含一个x-auth-token的Header,并且内容必须包含123。WireMock还提供了其他的匹配符,例如equalTo
, matches
, equalToXml
等。
这种方法的缺点是运行测试之前,一定要保证MockServer先启动起来。
还有一种方法,可以直接在Junit中,每个测试前自动启动MockServer,测试后自动关闭MockServer。
在工程目录中,也可以把__files和__mappings放置在src/test/resources下,通过文件配置。也可以通过代码配置。
无论哪种方式都需要通过添加@Rule
来指定WireMock的运行端口。添加了Rule之后,WireMock就会自动启动和关闭。
@Rule
public WireMockRule wireMockRule = new WireMockRule(WireMockConfiguration.options().port(8888));
在集成测试之前使用代码进行配置,例如在应用内部需要访问下游系统的/hello api得到结果。
@Test
public void should_get_hello() throws Exception {
stubFor(get(urlEqualTo("/hello"))
.willReturn(aResponse()
.withHeader("Content-Type", "text/plain")
.withBody("Hello world!")));
ResponseEntity<String> response = template.getForEntity(String.format("http://localhost:%s/", port),
String.class);
assertThat(response.getBody(), is("Hello world!"));
还可以使用WireMock来模拟网络延迟,支持固定延迟和随机延迟。
stubFor(get(urlEqualTo("/delayed")).willReturn(
aResponse()
.withStatus(200)
.withFixedDelay(2000)));
stubFor(get(urlEqualTo("/random/delayed")).willReturn(
aResponse()
.withStatus(200)
.withLogNormalRandomDelay(90, 0.1)));
WireMock还支持记录请求并重复,代理等功能,总之简单易用。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。