转载自:《Mock Server实践》闫帅:http://tech.meituan.com/mock-server-in-action.htmlphp
在美团服务端测试中,被测服务一般依赖于一系列的外部模块,被测服务与外部模块间经过REST API或是Thrift调用来进行通讯。要对被测服务进行系统测试,通常作法是,部署好全部外部依赖模块,由被测服务直接调用。然而有时被调用模块还没有开发完成,或者调用返回很差构造,这将影响被测系统的测试进度。为此咱们须要开发桩模块,用来模拟被调用模块的行为。最简单的方式是,对于每一个外部模块依赖,都建立一套桩模块。然而这样的话,桩模块服务将很是零散,不便于管理。Mock Server为解决这些问题而生,其提供配置request及相应response方式来实现通用桩服务。本文将专门针对REST API来进行介绍Mock Server的总体结构及应用案例。html
Mock Server由web配置页面Mock Admin及通用Mock Stub组成:Mock Admin提供了web UI配置页面,能够增长/删除请求来源IP到Mock环境的映射,能够对各套环境中的Mock规则进行CRUD操做;Mock Stub提供通用桩服务,对被测系统的各种REST API请求调用,返回预先定义好的模拟响应。为了提升桩服务的通吐,使得桩服务能在被测系统压力测试中获得好的表现,咱们开启了5个桩服务,经过Nginx作负载均衡。Mock Server的总体结构以下图所示。java
192.168.3.68
的请求,使用Mock环境名为闫帅的测试机环境
的Mock规则。<configuration> ... <mock id="716add4f-33f7-49ac-abf3-fc617712ffea" name="test001" author="yanshuai"> <request> <uri>/api/test/.*</uri> <method>GET|POST|PUT|DELETE</method> <parameters> <parameter name="name" value="test.*"/> ... </parameters> <headers> <header name="nb_deviceid" value="1E[0-9a-zA-Z]+"/> ... </headers> </request> <response delay="1000" real="false"> <statusCode>200</statusCode> <format>application/json;charset=UTF-8</format> <customHeaders> ... </customHeaders> <body>{"name":"闫帅"}</body> </response> </mock> ... </configuration>
当请求发送到Mock Stub时,Mock Stub会根据请求的来源IP找到对应的独立环境名,而后根据独立环境名获取全部预约义的Mock规则,遍历这些Mock规则,若是找到一条规则与接受到的请求匹配,那么返回预约义的模拟响应。若是找不到规则匹配,那么返回404错误。其中,规则匹配是根据请求中的uri/method/headers/parameters/body是否与Mock规则中定义的对应字段正则匹配来定的。android
建立/删除Mock规则,除了可经过Mock Admin页面配置外,Mock Server还提供了SDK方式,用户能够经过编码来使用Mock Server。web
<dependency> <groupId>com.sankuai.meituan.ep.mockserver</groupId> <artifactId>mock-client</artifactId> <version>1.0.6</version> </dependency>
// 构造Mock规则 MockRule rule = new MockRule(); rule.setMockName("test-" + System.currentTimeMillis()); // Mock name必须设置 rule.setAuthor("yanshuai"); // author必须设置,设置为代码编写者的mis帐号前缀,好比lining03 MockRequest mockRequest = new MockRequest(); mockRequest.setUri("/api/test/" + System.currentTimeMillis()); // Mock请求的uri必须设置 /** * Mock请求的方法必须设置 * 若是只有GET请求,则写成GET; * 若是有GET请求及PUT请求,则写成GET|PUT; * 即用|分割请求方法,不能有空格。 */ mockRequest.setMethod("POST|GET"); // 必要的话,设置Mock请求的匹配header List<MockRequestHeader> mockRequestHeaders = new ArrayList<MockRequestHeader>(); mockRequestHeaders.add(new MockRequestHeader("device", "android2.3")); mockRequest.setHeaders(mockRequestHeaders); // 必要的话,设置Mock请求的匹配参数 List<MockRequestParameter> mockRequestParameters = new ArrayList<MockRequestParameter>(); mockRequestParameters.add(new MockRequestParameter("wd", "123.*")); mockRequestParameters.add(new MockRequestParameter("version", "v1")); mockRequest.setParameters(mockRequestParameters); rule.setMockRequest(mockRequest); MockResponse mockResponse = new MockResponse(); mockResponse.setDelay(1000L); // 设置Mock响应的延时 mockResponse.setStatusCode(200); // 设置Mock响应的状态码 /** * 设置Mock响应的格式 * 若是是json返回,则为application/json;charset=UTF-8; * 若是是文本返回,则为text/plain:charset=UTF-8; * 若是是xml返回,则为text/xml;charset=UTF-8; * 若是是html返回,则为text/html;charset=UTF-8。 */ mockResponse.setFormat("application/json;charset=UTF-8"); List<MockResponseHeader> mockResponseHeaders = new ArrayList<MockResponseHeader>(); // 设置Mock响应的header mockResponseHeaders.add(new MockResponseHeader("customHeaderName", "customHeaderValue")); mockResponse.setMockResponseHeaders(mockResponseHeaders); mockResponse.setBody("{\"code\":200}"); // 设置Mock响应的body rule.setMockResponse(mockResponse); // 建立Mock规则 final MockClient client = new MockClient(); String id = client.addRule("default", rule); // default为环境名,若是使用别的环境,则填写别的环境名 // 调用被测服务的API,被测服务将调用Mock服务 // 省略调用代码... // 删除Mock规则 client.removeRule("default", id); // default为环境名,若是使用别的环境,则填写别的环境名
闫帅的测试机环境
对应的机器上,访问http://mock.ep.sankuai.com/api/v1/divisions,将延迟5s返回城市列表json串。
闫帅的测试机环境
对应的机器上,访问http://mock.ep.sankuai.com/cachier/paynotify返回值是failure;在支付php环境
对应的机器上,访问http://mock.ep.sankuai.com/cachier/paynotify返回值是success。