背景:測試效率低,工具使用難
由于我司所處的金融行業對資料準确性的要求非常高,是以我們測試人員在接口測試中,對無論是查詢還是寫入的所有資料,都必須先從資料庫中進行查詢然後再對比校驗。基于這些要求,測試人員在進行接口測試的同時,還需要及時查庫進行人工比對,這就導緻測試人員的測試效率低,一直無法進行大規模的回歸測試。
為了解決這個問題,我司之前嘗試過采用Pytest進行接口的自動化回歸測試,但Pytest對測試人員能力要求較高,而且對大量的接口測試用例難以形成有效管理,是以采用Pytest技術架構路線從也難以持續推進。
一次偶然的機會,我們得知了MeterSphere項目,不同于Postman和JMeter等工具,MeterSphere從平台的角度對測試進行管理,涵蓋測試跟蹤、接口測試、性能測試等功能,還相容JMeter等标準。同時我們還了解到,雖然MeterSphere是開源項目,但該項目組非常的活躍,對使用者的支援很積極,基于這些原因,我司決定嘗試使用MeterSphere進行接口自動化場景測試。
MeterSphere一站式開源持續測試平台項目官網:
https://www.metersphere.io
測試場景:複雜的進階腳本斷言
根據要求,我們需要将接口傳回的資料與資料庫查詢結果進行比對,是以需要取出相應的資料然後逐個字段值進行循環比較。循環斷言比對具有一定的複雜性,不同的場景需要采用不同的斷言方式,目前我司主要涉及到兩種斷言模式:BeanShell進階斷言和ForEach循環斷言。
以使用者注冊場景為例:注冊涉及到注冊人的身份證、手機号、使用者名稱、客戶類型、銀行卡号等資料,步驟為手機驗證→身份證驗證→設定密碼→注冊成功登陸→銀行卡資訊驗證→綁定成功,細節步驟如下:
■ 手機驗證:輸入手機号→發送驗證碼→資料庫查詢驗證碼→界面輸入驗證碼→校驗是否注冊過該手機号→手機驗證通過;
■ 身份證驗證:上傳身份證正反面→識别身份證有效性→維護個人資訊→驗證通過;
■ 設定密碼:設定密碼→是否滿足密碼要求→通過;
■ 注冊成功:注冊成功→進行登入;
■ 銀行卡資訊驗證:輸入銀行卡号→銀行卡号識别→發送驗證碼→資料庫查詢驗證碼→界面輸入驗證碼→校驗驗證碼是否正确→完成綁卡;
■ 銀行卡綁定成功:綁定成功→查詢使用者銀行卡資訊→校驗銀行卡資訊→驗證成功。
該場景涉及6個步驟、14個接口、斷言14個、提參14個,自定義腳本8個。
整體測試邏輯為使用者在接口請求資訊時需要從資料庫進行一次查詢,采用資料庫查詢結果和界面請求響應進行斷言比對,比對通過後則代表測試通過。
準備測試資料
因為測試涉及到人員資訊、手機号碼、身份證、銀行卡等敏感資訊,是以需要提前準備虛拟測試資料,在以往的測試模式中,都是采用Python腳本提前生成測試資料,然後人工複制至接口測試用例中,這給測試人員帶來了非常龐大的工作量。
現在,由于MeterSphere支援MockJS,通過MockJS可以提前模拟好人員資料、身份證等資訊。下圖為模拟的身份證資訊:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5yYwQWYiVDO0MGO2ETN4IDZjJjNyYjY4YTYmJWY3IDM08CX0AzLcZDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL4M3Lc9CX6MHc0RHaiojIsJye.png)
測試資料可以提前在項目管理的環境變量中進行定義或者使用接口自動化中的公共參數定義,後續在接口用例或者接口自動化場景中使用${Idcard}進行使用。
BeanShell腳本斷言
具體操作步驟如下:
Step 1:MeterSphere設定資料庫源,建立SQL請求,将查詢結果按照“存儲結果”進行存儲,傳回的資料示例為:
Step 2:建立接口請求,配置請求環境變量,擷取接口傳回的JSON資料。
傳回資料示例:
在此接口下添加後置腳本,提取接口響應body資料至環境變量message中(也可以采用JsonPath進行提取),如下圖:
Step 3:利用BeanShell斷言将資料庫傳回的資料和接口資料作比對。
添加斷言規則。
選擇類型為腳本,并編寫腳本儲存。
具體的斷言腳本如下,供大家參考,針對不同的場景需要微調:
import org.json.*; //導入需要用到的包 List share=vars.getObject("bankListInfo"); //擷取資料庫的存儲結果 String data = vars.get("message"); //擷取接口資料 JSONObject body = new JSONObject(data); //将接口資料轉化為JSON對象 String keyDesc = "rec"; //即将要斷言資料對象 if(!body.has(keyDesc) ){ //判斷接口資料是否包含“rec”對象 if(share == null || share.size() < 1){ //如果不包含斷言對象、資料庫傳回為空,則斷言失敗 AssertionResult.setFailure(true); //顯示斷言是否失敗 true是失敗,false為成功 AssertionResult.setFailureMessage("rec資訊與資料庫傳回均為空!!!");//顯示斷言資訊 return; } AssertionResult.setFailure(true); AssertionResult.setFailureMessage("rec資訊不存在"); return; } JSONArray rec = body.getJSONArray("rec");//上面已判斷目前對象已存在 if (rec == null || rec.length() < 1) { //上面已判斷目前對象是否資料為空 AssertionResult.setFailure(true); AssertionResult.setFailureMessage("rec資訊為空"); return; } if (share.size() != rec.length()) { //資料庫查詢結果與接口傳回結果數量不一緻,則斷言失敗 AssertionResult.setFailure(true); AssertionResult.setFailureMessage("資料庫查詢結果與接口傳回結果數量不一緻,資料庫數量:"+share.size()+" 接口傳回數量為:"+rec.length()); return; } String detail = "rec_Detail";//“rec”對象的子集 //beanshell顯示資料庫的示例資料: 測試資料:[{name=xxxx, description=nnnnn, id=wwwww, create_time=1609149399861}] //下面則為判斷接口傳回資料内容和資料庫傳回内容相同字段的值是否一緻 for (Object datum : share) { //這裡的判斷邏輯是以資料庫的傳回資料為準,循環周遊資料庫的傳回結果 Map map=(Map)datum; //單行資料,是以key:value存儲的 for(Object k:map.keySet()){ //周遊每一個字段 String key=k; //字段名稱 key Object value=map.get(k); //字段值 value boolean recFlag=false; //标志位:是否斷言成功 String resultMessage = ""; //斷言資訊 for (int i = 0; i < rec.length(); i++) {// 先判斷rec下的所有字段是否比對 JSONObject dataObject = rec.getJSONObject(i); if(value!=null && dataObject.has(key) ){ String resultStr = dataObject.getString(key); if((value.toString()).equals(resultStr) ){ recFlag=true; break; } resultMessage = "資料庫傳回結果:" + value + " JSON封包結果:" + resultStr; } if(!dataObject.has(detail)){//判斷 "rec"是否有 子集 continue; } JSONArray detailData = dataObject.getJSONArray("rec_Detail");//擷取子集資訊 if(detailData==null || detailData.length()<1){ //判斷 "rec"的子集是否為空 continue; } for(int n=0;n<detailData.length();n++){ //如果不為空,則繼續和資料庫傳回值作比對 JSONObject detailObject = detailData.getJSONObject(n); if(value!=null && detailObject.has(key) ){ String resultStr1 = detailObject.getString(key); if((value.toString()).equals(resultStr1) ){ //這裡全部當成字元串比對 recFlag=true; break; } resultMessage = "資料庫傳回結果:" + value + " JSON封包結果:" + resultStr; } } } if(!recFlag){ //設定比對結果 AssertionResult.setFailure(true); AssertionResult.setFailureMessage("存在不比對字段為:" + key + "---> " + resultMessage); } } }
Step 4:此場景運作結果如下圖。
ForEach循環斷言
ForEach控制器在JMeter中屬于邏輯控制器其中的一種,可以根據多個變量依次被循環調用,直到最後一個變量被調用即結束循環。在ForEach循環裡面将請求加入斷言,可以實作循環斷言的功能。
Step 1:對接口使用JsonPath的多行比對進行提取。
提取的封包如下:
Step 2:使用ForEach循環對接口提取值進行循環SQL請求,并添加腳本斷言。
ForEach循環下添加SQL請求:
SQL請求下添加腳本斷言。
Step 3:此場景報告如下圖。