Mock Server實踐
背景
在美團服務端測試中,被測服務通常依賴于一系列的外部模塊,被測服務與外部模塊間通過REST API或是Thrift調用來進行通信。要對被測服務進行系統測試,一般做法是,部署好所有外部依賴模塊,由被測服務直接調用。然而有時被調用模塊尚未開 發完成,或者調用返回不好構造,這將影響被測系統的測試進度。為此我們需要開發樁模塊,用來模擬被調用模塊的行為。最簡單的方式是,對于每個外部模塊依 賴,都創建一套樁模塊。然而這樣的話,樁模塊服務將非常零散,不便于管理。Mock Server為解決這些問題而生,其提供配置request及相應response方式來實現通用樁服務。本文將專門針對REST API來進行介紹Mock Server的整體結構及應用案例。
名詞解釋
- Mock規則:定義REST API請求及相應模擬響應的一份描述。
- Mock環境:根據請求來源IP來區分的Mock規則分組。Mock Server可以定義多套Mock環境,各套環境間相互隔離。同一個IP只能對應一個Mock環境,不同的IP可以對應同一個Mock環境。 </ul>
- 將請求來源IP到Mock環境名的映射存儲到mock-env.conf中,mock-env.conf的每一行定義了一條映射,如:
192.168.3.68 閆帥的測試機環境
這條映射表明來源是192.168.3.68的請求,使用Mock環境名為閆帥的測試機環境的Mock規則。 - 將配置的Mock規則存放到<對應Mock環境名>.xml中,下面部分展示了Mock規則的存儲格式。 </ul>
整體結構
Mock Server由web配置頁面Mock Admin及通用Mock Stub組成:Mock Admin提供了web UI配置頁面,可以增加/刪除請求來源IP到Mock環境的映射,可以對各套環境中的Mock規則進行CRUD操作;Mock Stub提供通用樁服務,對被測系統的各類REST API請求調用,返回預先定義好的模擬響應。為了提高樁服務的通吐,使得樁服務能在被測系統壓力測試中得到好的表現,我們開啟了5個樁服務,通過 Nginx做負載均衡。Mock Server的整體結構如下圖所示。
數據存儲
<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時,Mock Stub會根據請求的來源IP找到對應的獨立環境名,然后根據獨立環境名獲取所有預定義的Mock規則,遍歷這些Mock規則,如果找到一條規則與接受到 的請求匹配,那么返回預定義的模擬響應。如果找不到規則匹配,那么返回404錯誤。其中,規則匹配是根據請求中的uri/method/headers /parameters/body是否與Mock規則中定義的對應字段正則匹配來定的。
Mock Admin
- 打開Mock Admin配置頁面,如果尚未映射來源IP地址到環境,則點擊環境列表導航鏈接,進入環境列表頁面,點擊添加,輸入源IP及環境名,點擊確定按鈕,實現源IP到所設環境的映射。
- 點擊規則列表,規則列表頁面將默認羅列出default環境的所有Mock規則(如“語音登錄code獲取”規則)。重新選擇環境,可以羅列出所選環境中 的Mock規則。每個Mock規則都處于詳細信息展開的狀態。點擊“全部折疊”按鈕,將把所有的規則詳細信息給隱藏;點擊“全部展開”按鈕,將把所有的規 則詳細信息給展開。點擊“只顯示本人創建的規則”,將過濾得到mis賬戶用戶創建的規則。點擊“按創建時間排序”的開關按鈕,將實現Mock規則的升序/ 降序顯示。
- 點擊導航欄的“新建規則”選項,可以創建一個Mock規則,需要填寫規則名稱、請求及響應,并選中環境。對于請求,需要填寫URL及勾選Method,如 果要求對于符合某種規則的請求才被Mock,則填寫對應的Headers/Parameters/Body Like,這些數據都是正則匹配的形式。對于響應,如果勾選了“返回真實響應”,則只需要關注延時(延時是指返回請求需要的sleep時間,單位是毫 秒)。此時需要將請求的URL地址給寫完整了,需要包含host(IP)及port,不能只是path。如果不勾選“返回真實響應”,則將返回模擬響應。 Status Code填寫返回碼,比如200,404;Format選擇返回數據的格式,比如json,html等;還可以返回自定義的Headers及響應的 Body。點擊新建按鈕以后,如果創建成功,則提示成功創建,并跳轉到規則列表頁面。
- 對于一個創建好的Mock規則,可以點擊Action下拉菜單,進行操作。如果點擊“克隆”,則跳轉到“新建規則”頁面,并將克隆的Mock規則信息給填 充進去;點擊“編輯”,則跳轉到更新頁面,更新頁面填充了要編輯的Mock規則信息;點擊“刪除”,則彈出確認刪除對話框,點擊確定按鈕,將刪除此規則; 點擊取消按鈕,則取消刪除操作。如果此Mock規則處于詳細信息展開狀態,則可點擊折疊來隱藏詳細信息;如果處于詳細信息折疊狀態,則可點擊展開選項,將 顯示詳細信息。上移選項,可以將Mock規則上移一位;下移選項,可以將Mock規則下移一位。
- 修改被測服務的HTTP依賴,將依賴的IP和端口分別設置為mock.ep.sankuai.com和80,并重啟被測服務;
- 創建Mock規則;
- 調用被測服務的API,被測服務將調用Mock服務;
- 刪除Mock規則(可選)。 </ol>
- 在Maven工程pom.xml中添加mock-client依賴 </ul>
- 創建/刪除Mock規則 </ul>
- 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環境,同一接口,不同參數,可以有不同的Mock結果
按照下圖,依次創建這兩條規則(順序相關),然后在default環境對應的機器上,訪問 http://mock.ep.sankuai.com/user/v1/info?token=fake ,返回{"code":401,"type":"sys_err_auth_fail","message":"invalid token"};訪問 http://mock.ep.sankuai.com/user/v1/info?token=other ,返回{"user": {"id": 29008301,"mobile": "15001245907","isBindedMobile": 1}}。 - 真實請求延時Mock
按照下圖創建規則,在閆帥的測試機環境對應的機器上,訪問 http://mock.ep.sankuai.com/api/v1/divisions ,將延遲5s返回城市列表json串。 - 不同Mock環境,完全相同的接口參數,可以有不同的Mock結果
按照下圖創建規則,在閆帥的測試機環境對應的機器上,訪問 http://mock.ep.sankuai.com/cachier/paynotify 返回值是failure;在支付php環境對應的機器上,訪問 http://mock.ep.sankuai.com/cachier/paynotify 返回值是success。
</ol>
使用方式
編程使用
創建/刪除Mock規則,除了可通過Mock Admin頁面配置外,Mock Server還提供了SDK方式,用戶可以通過編碼來使用Mock Server。
<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規則 final MockClient client = new MockClient(); String id = client.addRule("default", rule); // default為環境名,如果使用別的環境,則填寫別的環境名
// 調用被測服務的API,被測服務將調用Mock服務 // 省略調用代碼...
// 刪除Mock規則
client.removeRule("default", id); // default為環境名,如果使用別的環境,則填寫別的環境名</pre>