1

上一篇中介绍了SpringBoot已经提供了集成测试功能,即:如果使用WebEnvironment.RANDOM_PORTTestRestTemplate,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根据需要返回我们指定的结果,达到测试的目的。

一般工程中采用两种方式:

  1. 自己写一个MockServer,部署到某个地方,请求和响应都自己实现,来模拟下游系统。

  2. 使用第三方库,这样的库非常多,如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
    }
}

有两种方式可以配置请求响应:

  1. 通过代码控制,稍后介绍。

  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还支持记录请求并重复,代理等功能,总之简单易用。


带着大馒头写代码
50 声望1 粉丝