一個基於多介面的業務自動化測試框架
這是一個成熟的框架,不是要讓別人當小白鼠,它已經先後在兩家公司的 5 條業務線進行了推廣應用,用例條數到了幾千條以上,並且從 18 年開始每天都在 CI/CD 流程中被呼叫執行。
已有那麼多介面測試框架,為什麼重複造輪子?首先,本框架如題目描述,適用於多介面的業務自動化測試,不是簡單的介面測試框架;其次框架始於 17、18 年,當時也沒有現在如此多的介面測試框架。
程式碼地址
框架介紹
介面自動化測試無疑是測試提效最為行之有效的方案,市面上的介面自動化測試框架很眾多,而本框架與其它框架的區別如以下:
- 用例程式碼編寫簡單,讓使用者精力集中在所測試系統的業務邏輯上,而 http 介面的定義,請求的傳送,測試報告資訊等都由框架完成
- 不只適用於單個介面的測試,同樣適用於多個介面組成的完整的業務邏輯的測試,這往往是介面自動化測試更應該做到的
- 登入等前置的業務操作也由框架完成,用例中只需引用相應 cookie
- 框架同樣支援環境、各類賬號以及其它測試物料資訊維護
- 簡單易用,java 小白也能在半小時內學會使用
框架結構
上手指南
工程結構說明
下面是一個論壇登入、瀏覽帖子、帖子點贊這樣一個簡單的業務場景進行舉例,如何用框架完成這一幾步操作的
定義 http 介面
介面定義是在 yml 檔案中,建議按照被測系統維護 yml 檔案
api:
globalVariables:
- UA: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36"
pioneers:
- name: testerhome登入
id: testerhomeLogin
priority: 1
path: https://$testerhomeHost/account/sign_in
method: post
headers: >-
\{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8","User-Agent":"$UA","x-requested-with":"XMLHttpRequest",
"cookie":"user_id=eyJfcmFpbHMiOnsibWVzc2FnZSI6Ik1UVTJOamM9IiwiZXhwIjpudWxsLCJwdXIiOiJjb29raWUudXNlcl9pZCJ9fQ%3D%3D--43f5d4f117b5459e67c85cc6c569820abb1e6068; _homeland_session=Y2ljEAtdhRcbEHaTSSHMb3%2FUyn0aLrFrHoEP8QVjVq%2BvXMCEi9n57WDgHBw40L%2Bo%2Fghe148%2B%2B429DbYDWNAiC4FBFYFnEghtzkQWPpKsOm21DZQkUDLvYqr4Z2ylpkiGHqjpppkhw0LLke61psEh7ZKQte3Ia3TTzTSu9ifDtHEl9FBlZUXNgwi%2F5kscioZqkobTyJpCGp5M4mSrLiunIZUHbgm05AuWa5%2Bu2TwgsxOfpdAumg6Q0SoT7ipMLaGaprobuP0Kj2q5ZH4CKqG7fb%2FU0WwzsTgTCtMXaWLz5WYHizGKRD5CWysSMseGn5I%3D--5LouY27EpiVkGarr--tpTXhgdFShw4Qyn6sThkpg%3D%3D",
"x-csrf-token":"zr6fgSyPS5nyqcwGdzD7R6T51aAK6L9Dv42Lao0CSPZo4jEn3pT5fNN2eTk84VdmqhzQasF+sdHQrvvxsLYSmg=="\}
parameters: user[login]=&user[password]=&user[remember_me]=0&user[remember_me]=1&commit=登入
extractors: \[{"name":"token","value":"cookies"}\]
requests:
- name: 讀帖子
id: topics
path: https://$testerhomeHost/topics/38484
method: get
headers: >-
\{"User-Agent":"$UA","Content-Type":"application/x-www-form-urlencoded","cookie":"$token","x-requested-with":"XMLHttpRequest","x-csrf-token":"r3E8899sEAEnqST2dmtIEluqG5C/nL/Rwp2l4ITtNDU3XpF4eULhClMRoWweMt6XWSmBn2H8fmPRas+CVkA/BA=="\}
- name: 點贊
id: likes
path: https://$testerhomeHost/likes
method: post
headers: >-
\{"User-Agent":"$UA","Content-Type":"application/x-www-form-urlencoded","cookie":"$token","x-requested-with":"XMLHttpRequest","x-csrf-token":"r3E8899sEAEnqST2dmtIEluqG5C/nL/Rwp2l4ITtNDU3XpF4eULhClMRoWweMt6XWSmBn2H8fmPRas+CVkA/BA=="\}
parameters: type=Topic&id=38484
如上,介面定義檔案大體分為三部分:globalVariables,pioneers,requests。
- globalVariables:定義全域性變數,為 key、value 形式
- pioneers 定義前置介面,用於定義登入等前置介面。程式啟動後、用例開始執行前,會自動先執行 pioneers 中定義的介面。 其中 name 隨意起;id 要唯一,建議按照介面請求地址的縮寫命名 id 屬性;priority,整數型別,當 pioneers 中定義了多個介面,執行時會按照 priority 屬性排序,之後順序執行。extractors:介面返回內容的提取,name,為提取的變數命名,後面介面可以透過 $name 名對其進行引用;value,變數的提取內容,支援提取 cookie 或返回 json 字串中的某個屬性 (填寫屬性的 json path)
- requests 定義介面,基本同 pioneers 部分,少了 extractors 部分。
說明:此處的介面請求引數可以透過抓包工具抓包獲取,然後複製到這裡。介面定義只需定義一次,在用例中隨意獲取,使用介面時,根據需要設定請求引數,未設定的請求引數按照此處定義的值作為預設值。
用例程式碼:
@Test(enabled = true, description = "開啟帖子詳情頁→點贊")
public void test() {
log.info("test start");
//請求例項1,開啟帖子詳情頁
Request request = Request.getInstance("topics");
//請求1傳送
Response response = request.doRequest();
//返回為html,取其中的x_csrf_token,後面點贊介面用
String html = response.asString();
Headers headers = response.getHeaders();
Map<String, String> cookies = response.getCookies();
Document document = Jsoup.parse(html);
Element metaElement = document.select("meta[name=csrf-token]").first();
String x_csrf_token = null;
if (metaElement != null) {
x_csrf_token = metaElement.attr("content");
}
//請求例項2,點贊介面
request = Request.getInstance("likes");
//更新cookie
request.addCookies(cookies);
if (x_csrf_token != null) {
request.addHeader("x-csrf-token",x_csrf_token);
}
//傳送點贊請求
response = request.doRequest();
assertThat(response.getStatusCode()).isGreaterThanOrEqualTo(200).as("返回狀態碼校驗");
}
測試報告
如下圖,用例相關介面的請求資訊、返回資訊也都由框架自動記錄在了報告中,如有其它需要內容輸出到測試報告,可以在用例中新增 Report.log("要新增內容");
其它
配置:如其它 spring 工程,配置檔案在 resources 目錄下,類似 pre、test 區分不同環境,application.properties 中定義一般的配置資訊(和環境無光),其中 pring.profiles.active=pre 來切換不同環境
-
測試範圍定義:測試用例由 testng 維護,如框架中所示,詳細使用方法參見 testng 官網
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite verbose="1" name="bulls-test" > <listeners> <listener class-name="com.bulls.qa.service.CustomListener"></listener> <listener class-name="com.bulls.qa.service.NoticeListener"></listener> </listeners> <test name="bulls自動化" preserve-order="true"> <parameter name="reruntimes" value="0"></parameter> <packages> </packages> <classes> <class name="com.bulls.qa.testcase.testerhome.Demo"> <methods> <include name="test"></include> </methods> </class> </classes> </test> </suite>
執行:專案入口 com.bulls.qa.BullsApplication.main
//打包
mvn clean -DskipTests=true package
//執行
java -jar target/bulls-0.6-SNAPSHOT.jar 測試範圍配置檔案.xml
如上面例子,測試範圍配置檔案可以配置多個,執行時指定測試範圍,如不指定預設使用打包的程式程式碼中的測試範圍配置檔案
- 測試報告:測試報導為單 html 檔案,方便 jenkins 配置展示,報告地址執行時所在目錄下 bulls.html
- 斷言,選用的斷言框架為 AssertJ,AssertJ 的強大無需贅述,詳細使用方法參見 AssertJ 官網
assertThat(response.jsonPath().getList("recommendations")).size().isGreaterThan(0).as("recommendations長度大於0");
assertThat(response.jsonPath().getBoolean("has_more")).isTrue().as("has_more為true");
assertThat(response.jsonPath().getList("recommendations")).as("recommendations長度大於0").size().isEqualTo(3);
List<String> types = JsonPath.from(response.asString()).getList("recommendations.item_type");
String[] strs = "product,product-ad-card,deal,ad,shopping-curated-collection,auto-generated-collection,video,campaign-banner,benefit,web-view".split(",");
assertThat(strs).containsAll(types).as("types在列舉範圍內");
- 傳送測試結果訊息通知,參見程式碼 NoticeListener,具體根據需要自行擴充套件
- 介面傳參設定,較複雜的介面引數設定
相關介面定義
- name: 編輯商品
id: itemEdit
path: http://$mnghost/item/edit
method: post
cookies: $XXXXXXCookies
headers: >-
\{"User-Agent":"$UA","Content-Type": "application/json"\}
parameters: >-
\{"itemId":"2904"\}
- name: 新增商品
id: itemSave
path: http://$mnghost/item/save
method: post
cookies: $XXXCookies
headers: >-
\{"User-Agent":"$UA","Content-Type": "application/json"\}
parameters: >-
\{"itemId":"2913","categoryIdList":[1],"topCategoryName":"美食","itemName":"autoTest goods","limitNumber":3,
"priceText":"","countDownCycle":"3","countDownLimit":"1","itemNo":"12sqw","delivery":"MANUAL",
"image":"//yun.XXXXXX.com/images/202005/4su03vvahd.jpg","detail":"","itemStatus":"ON","skuProperties":[],
"skuList":[{"id":3375,"stock":999999,"stockId":null,"sellingPrice":100,"originalPrice":100,"costPrice":100,
"realPayPrice":100,"properties":null,"skuNo":"1","skuEnable":true,"changeStock":0}],"supportCOD":true,
"originItemId":null,"merchantId":73,"tagIds":[],"id":2913,"topCategoryId":1,"itemShortName":"autoTest goo","url":null,
"minPrice":100,"stock":0,"isRecommend":false,"minSkuOriginalPrice":null,"minSkuPriceDiff":null,"maxPriceDiff":null,
"maxPriceDiffPrice":null,"maxPriceDiffOriginalPrice":null,"gmtModified":"2020-06-19 16:57:36","gmtModifyName":"測試專用",
"gmtModifyEmail":"test@XXXXXX.com.cn","mainRecomIds":null,"merchantName":"autoTestShop01","merchantDelivery":"MANUAL",
"imgHeight":[{"imgUrl":"http://yun.XXXXXX.com/images/202006/mj3yg07pj8.jpg","height":136},
{"imgUrl":"http://yun.XXXXXX.com/images/202006/d47ad68hhc.jpg","height":372}],"mainImgUrl":null,"itemIntroduce":null,
"saleLableUrl":null,"ssoDesc":null\}
相關程式碼
goodsId = 2904;
//編輯介面,獲取測試的商品資訊
Request request = Request.getInstance("itemEdit");
//直接設定,key-value形式
Response response = request.setParameter("itemId", goodsId).doRequest();
//庫存小於50,更新庫存
JsonPath jsonPath = response.jsonPath();
if (jsonPath.getBoolean("success") && jsonPath.getInt("data.stock") >= 50) {
// dosomething
}
Map<String, Object> map = response.jsonPath().getMap("data");
if (map == null) {
map = new HashMap<>();
}
map.put("itemId", goodsId);
map.put("stock", 9999999);
request = Request.getInstance("itemSave");
//遍歷介面的傳參結構定義,替換掉key完全匹配的那個map部分
request.setParameters(map);
//按照json path定位要設定的key
request.setParameter("$.skuList[0].stock", 9999999);
request.setParameter("$.skuList[0].changeStock", null);
//根據路徑刪除,路徑按json path
request.removeParameterByPath("$.skuList[0].stockId");
request.removeParameterByPath("$.skuList[0].id");
request.doRequest();
- 詳盡的 json-path 使用方法,參見 JsonPath 使用
相關文章
- 一種基於 cypress 的 UI 自動化測試框架UI框架
- T框架介紹(自動化測試框架)框架
- Python自動化測試框架介紹Python框架
- 基於Selenium+Python的web自動化測試框架PythonWeb框架
- 自動化測試框架框架
- android 5個自動化測試Ui框架AndroidUI框架
- 『與善仁』Appium基礎 — 8、Appium自動化測試框架介紹APP框架
- 基於Pytest豆瓣自動化測試【1】
- 基於Python豆瓣自動化測試【2】Python
- 基於 Htte 的 API 自動化測試API
- FAutoTest一個 H5、小程式自動化測試框架H5框架
- 自動化測試如此容易!多語言自動化測試框架 Selenium 程式設計(C#篇)框架程式設計C#
- 基於Python的介面自動化-unittest測試框架和ddt資料驅動Python框架
- postman實現介面的自動化測試Postman
- 測試開發之自動化篇-自動化測試框架設計框架
- 介面自動化測試框架 HttpFPT框架HTTP
- Python自動化測試框架-pytestPython框架
- Python 自動化測試框架unittestPython框架
- 利用tox打造自動自動化測試框架框架
- 四個類搞定分層自動化測試框架框架
- 基於AI的移動端自動化測試框架的設計與實踐AI框架
- [opendx] 基於 appium 的移動端 UI 自動化測試平臺-介紹篇APPUI
- web自動化測試框架-01 搭建基礎架構並執行一個樣例Web框架架構
- 推薦一款基於業務行為驅動開發(BDD)測試框架:Cucumber!框架
- 手把手教你基於 JMeter 開發一個自動化測試平臺 (2)JMeter
- 手把手教你基於 JMeter 開發一個自動化測試平臺 (1)JMeter
- 一文搞懂自動化測試框架設計框架
- Java + SikuliX 基於影像實現自動化測試Java
- 基於postman的api自動化測試實踐PostmanAPI
- 自動化測試的另外一個想法
- 自動化測試框架的AW模式框架模式
- UI自動化測試框架Cypress初探UI框架
- Selenium用法 - - 自動化測試介紹
- 為什麼很多基於 python 的自動化測試框架要用 excel 寫用例Python框架Excel
- 軟體自動化測試有什麼優勢?自動化測試框架有哪些?框架
- 2023年好用的自動化測試框架有哪些?如何提高自動化測試效果?框架
- Python自動化測試框架,誰才是你的唯一?Python框架
- Robot Framework自動化測試框架核心指南-如何做好自動化測試平臺框架的設計Framework框架