轉載自:《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。