WireMock进行集成测试

概述

在开发中,我们需要mock Http API,主要有两种使用场景:
1. 前后端分离开发,API first,可以提供虚拟接口,方便前端开发。
2. 集成测试依赖其他模块API,自己Mock Http接口。

WireMock就是一个比较比较适合做此场景的测试框架。它是java语言编写,可以在JUnit中使用,也可以独立部署HTTP服务运行。

一、Junit集成

示例

public class SampleTest {
    @Rule
    public WireMockRule wireMockRule = new WireMockRule(8089);

    @Test
    public void wireMockExampleTest() throws IOException, JSONException {
        String restposeJson = "{\"hello\":\"ok http\"}";
        stubFor(get(urlEqualTo("/my/resource"))
                .withHeader("Accept", equalTo("application/json"))
                .willReturn(aResponse()
                        .withStatus(200)
                        .withBody(restposeJson)));

        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url("http://localhost:8089/my/resource")
                .addHeader("Accept", "application/json")
                .build();
        Response response = client.newCall(request).execute();
        assertTrue(response.isSuccessful());
        assertEquals(response.code(), 200);
        JSONAssert.assertEquals(restposeJson, response.body().string(), false);

        verify(1, getRequestedFor(urlEqualTo("/my/resource"))
                .withHeader("Accept", equalTo("application/json")));
    }
}

wiremock模拟一个端口8089的服务,通过stbFor打桩,使用okHttpClient发起请求,对返回值断言和验证。

二、单独起服务

1. 启动

wireMock还提供了WireMock server,通过命令启动:

java -jar wiremock-standalone-2.6.0.jar


管理地址:
* http://localhost:8080/__admin/
* http://localhost:8080/__admin/swagger-ui/

2. 模拟API

POST请求

如果想尽快Mock一个API,可以通过curl、postman、chrome的wiremock插件快速POST数据到wiremock server。如下curl示例:

curl -X POST \
--data '{ "request": { "url": "/get/this", "method": "GET" }, "response": { "status": 200, "body": "Here it is!\n" }}' \
http://localhost:8080/__admin/mappings/new

POST成功后,检查模拟的API:

➜  ~ curl http://localhost:8080/get/this -i
HTTP/1.1 200 OK
Vary: Accept-Encoding, User-Agent
Transfer-Encoding: chunked
Server: Jetty(9.2.z-SNAPSHOT)

Here it is!

编写文件

启动mocke server 后,相同目录下生产__filesmappings两个目录:
* mappings:存放mock文件
* __files:存放mock文件中指定的body

cat mappings/hello_get.json

{
    "request": {
        "method": "GET",
        "url": "/api/mytest"
    },
    "response": {
        "status": 200,
        "body": "More content\n"
    }
}
curl http://localhost:8080/api/mytest
HTTP/1.1 200 OK
Vary: Accept-Encoding, User-Agent
Transfer-Encoding: chunked
Server: Jetty(9.2.z-SNAPSHOT)

More content

单独启动服务两个好处:
1. 快速模拟API,语言无关,可以给所有的开发团队使用
2. 由服务提供者编写并维护mappings下的json文件,方便其他模块开发和集成测试。

3. 其他方式启动

docker 启动

docker run -it  -p 8080:8080 -d rodolpheche/wiremock

maven plugin

<plugin>
  <groupId>uk.co.deliverymind</groupId>
  <artifactId>wiremock-maven-plugin</artifactId>
  <version>${wiremock-maven-plugin.version}</version>
  <executions>
     <execution>
        <goals>
           <goal>run</goal>
        </goals>
           <configuration>
              <dir>target/classes</dir>
              <params>--port=8081</params>
           </configuration>
     </execution>
  </executions>
</plugin>

mock 定义文件目录:
* src/main/resources/mappings/
* src/main/resources/__files/

三、spring-cloud-contract

spring-cloud-contract 是一个消费驱动设计工具。不管是自己mock还是通过一个中间服务mock,都有一个共同的问题,需要开发者同步维护mock,否则对一个老的mock API测试,结果毫无意义。

spring cloud contract 通过在生产者一方编写stub mapping,编译时自动生成一个jar包(http-server.1.0.1.stubs.jar),而消费方自动引入此包作为test的stub,这样,生产者每次修改代码,只要产生新stub,消费者都会自动获取,省去了双方沟通和一致的问题。

下面是一个简单示例:
Server / Producer side

package com.example;
import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc;
public class MvcTest {
  @Before
  public void setup() {
    RestAssuredMockMvc.standaloneSetup(new FraudDetectionController());
  }
}

src/test/resources/contracts使用groovy定义stub。
如:shouldMarkClientAsFraud.groovy

org.springframework.cloud.contract.spec.Contract.make {
  request {
    method 'PUT'
    url '/fraudcheck'
    body("""
    {
      "clientId":"1234567890",
      "loanAmount":99999
    }
    """)
    headers {
      header('Content-Type', 'application/vnd.fraud.v1+json')
    }
  }
response {
  status 200
  body("""
  {
    "fraudCheckStatus": "FRAUD",
    "rejectionReason": "Amount too high"
  }
  """)
  headers {
    header('Content-Type': 'application/vnd.fraud.v1+json')
  }
 }
}

maven test会做2个工作,通过上方定义的文件测试server和生成stub包。

Client / Consumer side
服务端使用:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureStubRunner(ids = {"com.example:http-server:+:stubs:8080"}, workOffline = true)
public class LoanApplicationServiceTests {
  • 加号(+): 表示最大版本号
  • workOffline=true:本地获取stub包


参考文档:
[1]. Wiremock-testing-mocking-over-wire-stubs
[2]. http://cloud.spring.io/spring-cloud-contract/
[3]. https://github.com/deliverymind/wiremock-maven-plugin
[4]. http://wiremock.org/
[5]. Videos:testing microservices with WireMock
[6]. http://wiremock.org/external-resources/

CONTENTS