美團接口自動化測試實踐
一、概述
1.1 接口自動化概述
眾所周知,接口自動化測試有著如下特點:
- 低投入,高產出。
- 比較容易實現自動化。
- 和UI自動化測試相比更加穩定。
如何做好一個接口自動化測試項目呢?
我認為,一個“好的”自動化測試項目,需要從“時間”、“人力”、“收益”這三個方面出發,做好“取舍”。
不能由於被測系統發生一些變更,就導致花費了幾個小時的自動化腳本無法執行。同時,我們需要看到“收益”,不能為了總想看到100%的成功,而少做或者不做校驗,但是校驗多了維護成本一定會增多,可能每天都需要進行大量的維護。
所以做好這三個方面的平衡並不容易,經常能看到做自動化的同學,做到最後就本末倒置了。
1.2 提高ROI
想要提高ROI(Return On Investment,投資回報率),我們必須從兩方面入手:
- 減少投入成本。
- 增加使用率。
針對“減少投入成本”
我們需要做到:
- 減少工具開發的成本。盡可能的減少開發工具的時間、工具維護的時間,盡可能使用公司已有的,或是業界成熟的工具或組件。
- 減少用例錄入成本。簡化測試用例錄入的成本,盡可能多的提示,如果可以,開發一些批量生成測試用例的工具。
- 減少用例維護成本。減少用例維護成本,盡量只用在頁面上做簡單的輸入即可完成維護動作,而不是進行大量的代碼操作。
- 減少用例優化成本。當團隊做用例優化時,可以通過一些統計數據,進行有針對性、有目的性的用例優化。
針對“增加使用率”
我們需要做到:
- 手工也能用。不只是進行接口自動化測試,也可以完全用在手工測試上。
- 人人能用。每一個需要使用測試的人,包括一些非技術人員都可以使用。
- 當工具用。將一些接口用例當成工具使用,比如“生成訂單”工具,“查找表單數據”工具。
- 每天測試。進行每日構建測試。
- 開發的在構建之後也能觸發測試。開發將被測系統構建後,能自動觸發接口自動化測試腳本,進行測試。
所以,我這邊開發了Lego接口測試平臺,來實現我對自動測試想法的一些實踐。先簡單瀏覽一下網站,了解一下大概是個什麽樣的工具。
首頁:
用例維護頁面:
自動化用例列表:
在線執行結果:
用例數量統計:
1.3 Lego的組成
Lego接口測試解決方案是由兩部分組成的,一個就是剛剛看到的“網站”,另一個部分就是“腳本”。
下面就開始進行“腳本設計”部分的介紹。
二、腳本設計
2.1 Lego的做法
Lego接口自動化測試腳本部分,使用很常見的Jenkins+TestNG的結構。
相信看到這樣的模型並不陌生,因為很多的測試都是這樣的組成方式。
將自動化測試用例存儲至MySQL數據庫中,做成比較常見的“數據驅動”做法。
很多團隊也是使用這樣的結構來進行接口自動化,沿用的話,那在以後的“推廣”中,學習和遷移成本低都會比較低。
2.2 測試腳本
首先來簡單看一下目前的腳本代碼:
public class TestPigeon {
String sql;
int team_id = -1;
@Parameters({"sql", "team_id"})
@BeforeClass()
public void beforeClass(String sql, int team_id) {
this.sql = sql;
this.team_id = team_id;
ResultRecorder.cleanInfo();
}
/**
* XML中的SQL決定了執行什麽用例, 執行多少條用例, SQL的搜索結果為需要測試的測試用例
*/
@DataProvider(name = "testData")
private Iterator<Object[]> getData() throws SQLException, ClassNotFoundException {
return new DataProvider_forDB(TestConfig.DB_IP, TestConfig.DB_PORT,
TestConfig.DB_BASE_NAME,TestConfig.DB_USERNAME, TestConfig.DB_PASSWORD, sql);
}
@Test(dataProvider = "testData")
public void test(Map<String, String> data) {
new ExecPigeonTest().execTestCase(data, false);
}
@AfterMethod
public void afterMethod(ITestResult result, Object[] objs) {...}
@AfterClass
public void consoleLog() {...}
}
有一種做法我一直不提倡,就是把測試用例直接寫在Java文件中。這樣做會帶來很多問題:修改測試用例需要改動大量的代碼;代碼也不便於交接給其他同學,因為每個人都有自己的編碼風格和用例設計風格,這樣交接,最後都會變成由下一個同學全部推翻重寫一遍;如果測試平臺更換,無法做用例數據的遷移,只能手動的一條條重新輸入。
所以“測試數據”與“腳本”分離是非常有必要的。
網上很多的範例是使用的Excel進行的數據驅動,我這裏為什麽改用MySQL而不使用Excel了呢?
在公司,我們的腳本和代碼都是提交至公司的Git代碼倉庫,如果使用Excel……很顯然不方便日常經常修改測試用例的情況。使用MySQL數據庫就沒有這樣的煩惱了,由於數據與腳本的分離,只需對數據進行修改即可,腳本每次會在數據庫中讀取最新的用例數據進行測試。同時,還可以防止一些操作代碼時的誤操作。
這裏再附上一段我自己寫的DataProvider_forDB
方法,方便其他同學使用在自己的腳本上:
import java.sql.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* 數據源 數據庫
*
* @author yongda.chen
*/
public class DataProvider_forDB implements Iterator<Object[]> {
ResultSet rs;
ResultSetMetaData rd;
public DataProvider_forDB(String ip, String port, String baseName,
String userName, String password, String sql) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
String url = String.format("jdbc:mysql://%s:%s/%s", ip, port, baseName);
Connection conn = DriverManager.getConnection(url, userName, password);
Statement createStatement = conn.createStatement();
rs = createStatement.executeQuery(sql);
rd = rs.getMetaData();
}
@Override
public boolean hasNext() {
boolean flag = false;
try {
flag = rs.next();
} catch (SQLException e) {
e.printStackTrace();
}
return flag;
}
@Override
public Object[] next() {
Map<String, String> data = new HashMap<String, String>();
try {
for (int i = 1; i <= rd.getColumnCount(); i++) {
data.put(rd.getColumnName(i), rs.getString(i));
}
} catch (SQLException e) {
e.printStackTrace();
}
Object r[] = new Object[1];
r[0] = data;
return r;
}
@Override
public void remove() {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
2.3 配置文件
上面圖中提到了“配置文件”,下面就來簡單看一下這個XML配置文件的腳本:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Pigeon Api測試" parallel="false">
<test name="xxx-xxx-service">
<parameter name="sql"
value="SELECT * FROM API_PigeonCases
WHERE team_id=2
AND isRun=1
AND service=‘xxx-xxx-service‘
AND env=‘beta‘;"/>
<classes>
<class name="com.dp.lego.test.TestPigeon"/>
</classes>
</test>
<listeners>
<listener class-name="org.uncommons.reportng.HTMLReporter"/>
<listener class-name="org.uncommons.reportng.JUnitXMLReporter"/>
</listeners>
</suite>
對照上圖來解釋一下配置文件:
- SQL的話,這裏的SQL主要決定了選取哪些測試用例進行測試。
- 一個標簽,就代表一組測試,可以寫多個標簽。
- “listener”是為了最後能夠生成一個ReportNG的報告。
- Jenkins來實現每日構建,可以使用Maven插件,通過命令來選擇需要執行的XML配置。
這樣做有什麽好處呢?
使用SQL最大的好處就是靈活
如上面的這個例子,在數據庫中會查詢出下面這56條測試用例,那麽這個標簽就會對這56條用例進行逐一測試。
多標簽時,可以分組展示
使用多個標簽來區分用例,最大的好處就是也能在最後的報告上,達到一個分組展示的效果。
報告更美觀豐富
由於使用了ReportNG
進行報告的打印,所以報告的展示要比TestNG自帶的報告要更加美觀、並且能自定義展示樣式,點開能看到詳細的執行過程。
如果有執行失敗的用例,通常報錯的用例會在最上方優先展示。
支持多團隊
當兩個團隊開始使用時,為了方便維護,將基礎部分抽出,各個團隊的腳本都依賴這個Base包,並且將Base包版本置為“SNAPSHOT版本”。使用“SNAPSHOT版本”的好處是,之後我對Lego更新,各個業務組並不需要對腳本做任何改動就能及時更新。
當更多的團隊開始使用後,比較直觀的看的話是這個樣子的:
每個團隊的腳本都依賴於我的這個Base包,所以最後,各個業務團隊的腳本就變成了下面的這個樣子:
可以看到,使用了Lego之後:
- 沒有了Java文件,只有XML文件
- xml中只需要配置SQL。
- 執行和調試也很方便。
- 可以右鍵直接執行想要執行的測試配置。
- 可以使用maven命令執行測試:
mvn clean test -U -Dxml=xmlFileName
。- 通過參數來選擇需要執行的xml文件。
- 也可以使用Jenkins來實現定時構建測試。
由於,所有測試用例都在數據庫所以這段腳本基本不需要改動了,減少了大量的腳本代碼量。
有些同學要問,有時候編寫一條接口測試用例不只是請求一下接口就行,可能還需要寫一些數據庫操作啊,一些參數可能還得自己寫一些方法才能獲取到啊之類的,那不code怎麽處理呢?
下面就進入“用例設計”,我將介紹我如何通過統一的用例模板來解決這些問題。
三、用例設計
3.1 一些思考
我在做接口自動化設計的時候,會思考通用、校驗、健壯、易用這幾點。
通用
- 簡單、方便
- 用例數據與腳本分離,簡單、方便。
- 免去上傳腳本的動作,能避免很多不必要的錯誤和維護時間。
- 便於維護。
- 模板化
- 抽象出通用的模板,可快速拓展。
- 數據結構一致,便於批量操作。
- 專人維護、減少多團隊間的重復開發工作。
- 由於使用了統一的模板,那各組之間便可交流、學習、做有效的對比分析。
- 如果以後這個平臺不再使用,或者有更好的平臺,可快速遷移。
- 可統計、可拓展
- 可統計、可開發工具;如:用例數統計,某服務下有多少條用例等。
- 可開發用例維護工具。
- 可開發批量生成工具。
校驗
在寫自動化腳本的時候,都會想“細致”,然後“寫很多”的檢查點;但當“校驗點”多的時候,又會因為很多原因造成執行失敗。所以我們的設計,需要在保證充足的檢查點的情況下,還要盡可能減少誤報。
- 充足的檢查點
- 可以檢查出被測服務更多的缺陷。
- 盡量少的誤報
- 可以減少很多的人工檢查和維護的時間人力成本。
- 還要
- 簡單、易讀。
- 最好使用一些公式就能實現自己想要的驗證。
- 通用、靈活、多樣。
- 甚至可以用在其他項目的檢查上,減少學習成本。
健壯
執行測試的過程中,難免會報失敗,執行失敗可能的原因有很多,簡單分為4類:
- 被測系統出錯,這部分其實是我們希望看到的,因為這說明我們的自動化測試真正地發現了一個Bug,用例發揮了它的價值,所以,這是我們希望看到的。
- 測試工具出錯,這部分其實是我們不希望看到的,因為很大可能我們今天的自動化相當於白跑了。
- 測試數據錯誤,這是我們要避免的,既然數據容易失效,那我在設計測試平臺的時候,就需要考慮如果將所有的數據跑“活”,而不是只寫“死”。
- 不可抗力,這部分是我們也很無奈的,但是這樣的情況很少發生。
那針對上面的情況:
- 參數數據失效
- 支持實時去數據庫查詢。
- 支持批量查。
- IP進場發生變更
- 自動更新IP。
- 靈活、可復用
- 支持批量維護。
- 接口測試執行前生成一些數據。
- 接口執行完成後銷毀一些數據。
- 支持參數使用另一條測試用例的返回結果。
- 支持一些請求參數實時生成,如token等數據,從而減少數據失效的問題。
通過這些手段,提高測試用例的健壯性,讓每一條自動化測試用例都能很好的完成測試任務,真正發揮出一條測試用例的價值。
易用
- 簡單
- 功能強大,但要人人會用。
- 非技術人員也要會用。
- 減少代碼操作
- 讓自動化開發人員註意力能更多的放在用例本身,而不是浪費在無關緊要的開發工作上面。
- 還要
- 配置能復用。
- 通用、易學。
- 一些數據能自動生成。
3.2 Lego接口自動化測試用例
說了這麽多,那我們來看一下一條Lego接口測試用例的樣子。
一條Lego自動用例執行順序大概是如下圖這樣:
簡單區分一下各個部分,可以看到:
那上面圖中提到了兩個名詞:
- “參數化”
- “前後置動作”
下面會先對這兩個名詞做一個簡單的介紹。
3.3 參數化
比如一個請求需要用到的參數。
{
"sync": false,
"cityId": 1,
"source": 0,
"userId": 1234,
"productId": 00004321
}
這個例子中有個參數"productId": 00004321
,而由於測試的環境中,表單00004321很可能一些狀態已經發生了改變,甚至表單已經刪除,導致接口請求的失敗,那麽這時候,就很適合對"productId": 00004321
進行參數化,比如寫成這樣:
{
"sync": false,
"cityId": 1,
"source": 0,
"userId": 1234,
"productId": ${myProductId}
}
所以對“參數化”簡單的理解就是:
通過一些操作,將一個“值”替換掉測試用例裏的一個“替代字符”
${myProductId}
的值可以通過配置獲取到:
- Key-Value
- 配置 Value=00004321。
- SQL獲取
- 執行一個select語句來實時查詢得到可用ID。
- 已有測試用例
- 某個接口接口測試用例的返回結果。
“參數化”實例
下面我們來看一個“參數化”的實例:
(1) 首先我們在參數化維護頁面中新建一個參數化,shopdealid
。
通過配置我們可以看到這個參數的值,是執行了一條SQL後,取用執行結果中DealID
字段的值。
(2) 在用例中,將需要這個表單號的地方用${shopdealid}替代。
那在編寫測試用例的時候,大家可以看一下這個放大的圖片,在這裏的ProductID的值並不是硬代碼一個固定的表單號,而是選擇了剛才配置的參數化數據。
(3) 執行結果中,${shopdealid} 變為實時查詢數據庫的來的一個真實的表單號。
從結果中可以看到,我們的這個參數被替換成了一個有效的值,而這個值就是我們剛剛配置的那個SQL實時查詢而來的。
“參數化”的場景
多個測試用例使用同一個參數進行測試
如50條測試用例都使用同一個id作為參數進行測試,這時候我們需要變更這個id。
無參數化時:
- 需要修改50次,即每條測試用例中的id都得進行修改。
- 可能會有遺漏。
有參數化時: - id部分用 ${myID} 替代。
- 需要修改的話,在“參數化維護”頁面中維護 ${myID}這條數據就可以。修改一次,所有使用${myID}的用例都配置完成。
測試數據過期導致測試用例執行失敗
如一條用例參數需要傳入token,但是Token會因為時間問題而導致過期,這時候用例就失敗了。
無參數化時:
- 經常修改Token,或是寫一段id轉Token的代碼。
- 方法可能會重復編寫。
- 多個團隊之間可能實現方式也不同。
有參數化時:
- 使用參數化工具,Lego統一管理。
- 維護一個參數化 如:
${測試用Token} = id:123
。
數據庫獲取有效測試數據
參數中需要傳入DealId作為參數,寫死參數的話,如果這個DealId被修改引起失效,那這條測試用例就會執行失敗。
不使用Lego時:
- 測試環境中,一個訂單時常會因為測試需要被修改數據,導致單號失效,最後導致自動化失敗。
- 編寫相關代碼來做好數據準備工作。
- 在代碼中編寫讀取數據庫的方法獲取某些內容。
在Lego上的方案:
- 使用參數化,實時獲取sql結果,查詢出一條符合條件的dealId來實現。
- 使用參數化,調用寫好的“生成訂單”接口用例實現,拿單號來實現。
- 前後置動作,插入一條滿足條件的數據。
3.4 前後置動作
“前後置動作”的概念就比較好理解了:
在接口請求之前(或之後),執行一些操作
目前前後置動作支持6種類型:
- 數據庫SQL執行
- 有時候在執行接口請求前,為了保證數據可用,可能需要在數據庫中插入或刪除一條信息,這時候就可以使用前後置動作裏的“執行SQL語句”類型,來編寫在接口請求前(後)的 Insert 和 Delete 語句。
- 已有測試用例執行
- 比如當前測試用例的請求參數,需要使用另一條測試用例的返回結果,這時候就可以使用“執行測試用例”類型,寫上Lego上某條測試用例的ID編號,就可以在當前用例接口請求前(後)執行這條測試用例。
- 前後置動作中測試用例的返回結果可以用於當前用例的參數,對測試用例返回結果內容的獲取上,也支持JsonPath和正則表達式兩種方式。
- MQ消息發送
- 在接口請求前(後)發送MQ消息。
- HTTP請求
- 等待時間
- 自定義的Java方法
- 如果上面的方法還滿足不了需求,還可以根據自己的需要,編寫自己的Java方法。
- 可以在Lego-Kit項目中,編寫自己需要的Java方法,選擇“執行Java方法”,通過反射實現自定義Java方法的執行。
這裏的SQL同時支持Select操作,這裏其實也是做了一些小的設計,會將查詢出來的全部的結果,放入到這個全局Map中。
比如查詢一條SQL得到下表中的結果:
id | name | age | number |
---|---|---|---|
0 | 張三 | 18 | 1122 |
1 | 李四 | 30 | 3344 |
那我們可以使用下面左邊的表達式,得到對應的結果:
${pre.name}
---- 得到 “張三”?${pre.age}
---- 得到 18${pre.number}
---- 得到 1122
也可以用:
${pre.name[0]}
---- 得到 “張三”${pre.age[0]}
---- 得到 18${pre.number[0]}
---- 得到 1122${pre.name[1]}
---- 得到 “李四”${pre.age[1]}
---- 得到 30${pre.number[1]}
---- 得到 3344
這樣的設計,更加幫助在用例設計時,提供數據準備的操作。
“前後置動作”實例
(1) 首先我們在前後置維護頁面中新建一個動作,獲取庫存上限未賣光團單
。
這個配置也是可以支持在線調試的,在調試中,可以看到可以使用的參數化:
(2) 在測試用例中的前置動作,添加獲取庫存上限未賣光團單
。
這樣就可以在整個測試用例中,使用${pre.ProductID}
,來替換掉原有的數據信息。
(3) 最後請求接口,返回了執行成功 。
Q & A
Q:那如果同樣是獲取三個參數,使用3個“參數化的Select操作”和使用1個“前置動作的Select操作”又有什麽不同呢?
A: 不同在於執行時間上。
比如,我們查詢最新的有效團單的“單號”“下單人”和“手機號”三個字段。
使用3個“參數化的Select操作”:可能當執行${單號}的時候得到的訂單號是“10001”,但是當執行到${下單人}的時候,可能有誰又下了一單,可能取到的下單人變成了“10002”的“李四”而不是“10001”的“張三”了,最後可能“單號”“下單人”和“手機號”三個字段去的數據並非同一行的數據。
而使用“前置動作的Select操作”:就可以避免上面的問題,因為所有字段的數據是一次性查詢出來的,就不會出現錯位的情況。
Q : 那“參數化的Select操作”和“前置動作的Select操作”這樣不同的取值時機又有什麽好用之處呢?
A : 由於“前置動作”一定是接口請求前執行,“參數化”一定是用到的時候才執行這樣的特性。
所以在檢查點中,如果要驗證一個數據庫字段在經過接口調用後發生了變更,那使用“前置動作”和“參數化”同時去查詢這個字段,然後進行比較,不一致就說明發生了變化。
所以根據使用場景,選擇合適的參數化方式,很重要,選擇對了,能大大提升測試用例的測試數據健壯性。
3.5 執行各部分
回到一開始的流程圖,可以按照一類一類來看執行過程。
測試發起
測試發起基本還是使用的Jenkins,穩定、成熟、簡單、公司工具組支持,也支持從Lego的Web頁面進行執行操作。
數據 / 環境準備
使用 @DataProvider 的方式,從DB數據庫中讀取測試用例,逐一執行進行測試。
測試執行
在正式執行測試用例之前,會先進行一波參數替換的動作,在調用接口之後,還會執行一次參數替換動作。
參數替換後會進行前置動作的執行,然後在調用接口之後還會執行測試後動作,最後執行後置動作。
接口請求這部分就沒什麽好說的了,就是通過接口請求的參數,請求對應的接口,拿到返回結果。
這裏的話是為了方便通用,所以要求返回的結果都是使用的String類型。這樣做最大的好處就是。比如說我現在有一種新的接口類型需要接入。那只需要寫一個方法能夠請求到這個接口,並且拿到String類型的返回結果,就可以很快將新的接口類型接入Lego測試平臺進行接口測試。
檢查點校驗
檢查點部分是一條自動化測試用例的精髓,一條自動化測試用例是否能真正的發揮它的測試功能,就是看QA對這條測試用例的檢查點編寫是否做了良好設計。在Lego平臺上,目前我擁有的檢查點有6種不同的類型。
- 異常檢查點
- 當返回結果為異常時,則會報錯。
- 但是有時候為了做異常測試,可以將這個檢查點關掉。
- 不為空檢查點
- 顧名思義,當出現""、"[]"、"{}"、null 這樣的的結果,都會報錯。也可以根據自己用例的實際情況關閉。
- 包含檢查點
- 不包含檢查點
- “包含”和“不包含”檢查點是將接口的返回結果作為一個String類型來看,檢查所有返回內容中是否“包含”或“不包含”指定的內容。
- 數據庫參數檢查點
- 顧名思義,不做過多的解釋了。
- JsonPath檢查點
- 這是我在Lego上設計的最具有特色的一種檢查點類型。
JsonPath的基本寫法是:{JsonPath語法}==value
JsonPath的語法和XPath的語法差不多,都是根據路徑的方法找值。這裏也是主要是針對返回結果為JSON數據的結果,進行檢查。
具體的JsonPath語法可以參考:https://github.com/json-path/JsonPath
說完了"JsonPath的語法",現在說一下"JsonPath檢查點的語法","JsonPath檢查點的語法"是我自己想的,主要針對以下幾種數據類型進行校驗:
(1) 字符串類型結果檢驗
- 等於:
==
- 不等於:
!==
- 包含:
=
- 不包含:
!=
例如:
{$.[1].name}==aa
:檢查返回的JSON中第2個JSON的name字段是否等於aa。{$..type}==‘14‘
:檢查返回的JSON中每一個JSON的name字段是否等於aa。{$.[1].type}==14 && {$.[1].orderId}==106712
:一條用例中多個檢查用&&連接。{$..orderId}!==12
:檢查返回的JSON中每個JSON的orderId字段是否不等於12。{$..type}=1
:檢查返回的JSON中每個JSON的type字段是否包含1。{$.[1].type}!=chenyongda
:檢查返回的JSON中第2個JSON的type字段是否不包含chenyongda。
(2) 數值校驗
- 等於:
=
- 大於:
>
- 大於等於:
>=
- 小於:
<
- 小於等於:
<=
例如:
- {$.[0].value}<5:檢查返回的JSON中第1個JSON的value字段的列表是否小於3。
- {$.[1].value}>4:檢查返回的JSON中第2個JSON的value字段的列表是否大於4。
(3) List結果檢驗
- list長度:
.length
- list包含:
.contains(param)
- list成員:
.get(index)
例如:
{$..value}.length=3
:檢查返回的JSON中每個JSON的value字段的列表是否等於3。{$.[0].value}.length<5
:檢查返回的JSON中第1個JSON的value字段的列表是否小於3。{$.[1].value}.length>4
:檢查返回的JSON中第2個JSON的value字段的列表是否大於4。{$..value}.contains(‘222‘)
:檢查返回的JSON中每個JSON的value字段的列表是否包含222字符串。{$.[0].value}.contains(1426867200000)
:檢查返回的JSON中第1個JSON的value字段的列表是否包含1426867200000。{$.[0].value}.get(0)==‘222‘
:檢查返回的JSON中第1個JSON的value字段的列表中第1個內容是否等於222。{$..value}.get(2)=‘22‘
:檢查返回的JSON中每個JSON的value字段的列表中第3個內容是否包含22。
(4) 時間類型處理
時間戳轉日期時間字符串:.todate
例如:
{$..beginDate}.todate==2015-12-31 23:59:59
:檢查返回的JSON中beginDate這個時間戳轉換成日期後是否等於2015-12-31 23:59:59。
當JsonPath返回的結果是列表的形式時
檢查點 | 檢查點等號左邊 | 期望值 | 驗證效果 |
---|---|---|---|
{$.value}=="good" | [‘good‘, ‘good‘, ‘bad‘, ‘good‘] | "good" | 作為4個檢查點,會拿列表裏的每個對象逐一和“期望值”進行檢驗,每一次對比都是一個獨立的檢查點。 |
{$.value}==["good"] | [‘good‘, ‘good‘, ‘bad‘, ‘good‘] | ["good"] | 作為1個檢查點,作為一個整體做全量比對。 |
{$.value}==[‘a‘, ‘b‘] | [[‘a‘, ‘b‘],[‘a‘, ‘b‘],[‘a‘, ‘b‘, ‘c‘]] | [‘a‘, ‘b‘] | 作為3個檢查點,道理和1一樣,列表中的數據分別和期望值做比較。 |
除此之外,還有非常多的花樣玩法
JsonPath中的檢查支持“參數化”和“前後置動作”,所以會看到很多如:
{$.param}=‘${param}‘ && {$.param}==${pre.param}
這樣的檢查點:
“參數化”和“前後置動作”也支持遞歸配置,這些都是為了能夠讓接口自動化測試用例寫的更加靈活好用。
測試結果
使用ReportNG可以打印出很漂亮的報告。
報告會自定義一些高亮等展示方式,只需要在ReportNG使用前加上下面的語句,就可以支持“輸出逃逸”,可使用HTML標簽自定義輸出樣式。
System.setProperty("org.uncommons.reportng.escape-output", "false");
後期優化
當使用Jenkins執行後,通過Jenkins API 、和Base包中的一些方法,定時獲取測試結果,落數據庫,提供生成統計圖表用。
四、網站功能
4.1 站點開發
既然打算做工具平臺了,就得設計方方面面,可惜人手和時間上的不足,只能我一人利用下班時間進行開發。也算是擔任了Lego平臺的產品、後端開發、前端開發、運維和測試等各種角色。
Jenkins+TestNG+ReportNG+我自己開發的基本接口自動化測試Base jar包,基本上沒什麽太大難度。但是站點這塊,在來美團之前,還真沒開發過這樣的工具平臺,這個算是我的第一個帶Web界面的工具。邊Google邊做,沒想到不久還真的架起來了一個簡易版本。
使用 Servlet + Jsp 進行開發,前端框架使用Bootstrap,前端數據使用jstl,數據庫使用MySQL,服務器使用的公司的一臺Beta環境Docker虛擬機,域名是申請的公司內網域名,並開通北京上海兩側內網訪問權限。
功能上基本都是要滿足的,界面上,雖然做不到驚艷吧,但是絕對不能醜,功能滿足,但是長得一副80年代的界面,我自己都會嫌棄去使用它,所以界面上我還是花了一些時間去調整和設計。熟練以後就快多了。
4.2 整體組成
目前Lego由五個不同的項目組成,分別是“測試腳本”、“Lego-web頁面項目”、“用於執行接口測試的base包”、“小工具集合Lego-kit”和“lego-job”,通過上圖可以看出各項目間的依賴關系。
細化各個項目的功能,就是下圖:
簡單來說,網站部分和腳本是分離的,中間的紐帶是數據庫。所以,沒有網站,腳本執行一點問題也沒有;同樣的,網站的操作,和腳本也沒有關系。
4.3 使用-日常維護
Step 1
每天上班來會收到這樣的測試郵件,通過郵件能知道昨晚執行的情況。如果有報錯,可以點擊“詳細報告鏈接”,跳轉到在線報告。
Step 2
在現報告可以直接看到執行報錯的信息,然後點擊“LEGO維護傳送門”,可以跳轉到Lego站點上,進行用例維護。
Step 3
跳轉到站點上以後,可以直接展示出該條測試用例的所有信息。定位,維護、保存,維護用例,可以點擊“執行”查看維護後的執行結果,維護好後“保存”即可。
僅僅3步,1~2分鐘即可完成對一條執行失敗的用例進行定位、調試和維護動作。
4.4 用例編輯
通過頁面,我們就可以對一條測試用例進行:
- 新建
- 復制
- 編輯
- 刪除
- 是否放入每日構建中進行測試
4.5 在線調試
lego-web項目同樣的使用base進行的用例執行,所以執行結果和打印都與腳本執行的一致的。
4.6 用例生成工具
為了更方便的寫用例,針對部分接口開發了一鍵批量生成用例的小工具。
4.7 執行結果分析
通過Jenkins接口、Base包中基礎Test方法,將結果收集到數據庫,便於各組對測試結果進行分析。
這是每天執行後成功率走勢圖:
也可以按月進行統計,生成統計的圖表,幫助各個團隊進行月報數據收集和統計。
4.8 失敗原因跟蹤
有了能直觀看到測試結果的圖表,就會想要跟蹤失敗原因。
所以在成功率數據的右邊,會有這樣的跟蹤失敗原因的入口,也可以很直觀地看到哪一些失敗的原因還沒有被跟蹤。點開後可以對失敗原因進行記錄。
最後會有生成圖表,可以很清晰地看到失敗原因以及失敗類型的占比。
4.9 代碼覆蓋率分析
結合Jacoco,我們可以對接口自動化的代碼覆蓋率進行分析。
在多臺Slave機器上配置Jacoco還是比較復雜的,所以可以開發覆蓋率配置輔助工具來幫助測試同學,提高效率。
4.10 用例優化方向
除了上面的圖表,還會給用例優化提供方向。
通過用例數量統計的圖表,我們可以知道哪些服務用例還比較少,哪些環境的用例還比較少,可以比較有針對性的進行測試用例的補充。
通過失敗原因的圖表,我們可以改善自己用例中的“參數化”和“前後置動作”的使用,增加測試用例的健壯性。
通過線上接口調用量排序的圖表。我們可以有效的知道優先維護哪些服務的測試用例,通過表格中,我們可以看到,哪些服務已經覆蓋了測試用例,哪些沒有被覆蓋, 給各組的QA制定用例開發計劃,提供參考。
同時在維護接口自動化測試的時候,都會看到用例評分的情況,來協助QA提高用例編寫的質量。
4.11 收集反饋/學習
還做了“需求白板”,用來收集使用者的需求和Bug。除此之外,Lego平臺已經不只是一個接口測試的平臺,還可以讓想學習開發的QA領任務,學習一些開發技巧,提高自己的代碼能力。
五、總結
- 為了減少開發成本,使用比較常見的Jenkins+TestNG的腳本形式。
- 為了簡化code操作,使用DB進行測試用例存儲,並抽象出用例摸版。
- 為了減低新建用例成本,開發“用例維護頁面”和“一鍵生成”等工具。
- 為了減低維護成本,加跳轉鏈接,維護一條用例成本在幾分鐘內。
- 為了增加用例健壯性,設計了“參數化”、“前後置動作”等靈活的參數替換。
- 為了易用和兼容,統一“返回結果”類型,統一“檢查點”的使用。
- 為了接口自動化用例設計提供方向,結合Jacoco做代碼覆蓋率統計,並開發相關配置工具
- 為了便於分析數據,從DOM、CAT、Jenkins上爬各種數據,在頁面上用圖表展示。
- 為了優化用例,提供“用例打分”、“線上調用量排行”等數據進行輔助。
美團接口自動化測試實踐