Lego- 美團接口自動化測試實踐

https://www.infoq.cn/article/G8CN7zGCqUeVhRr1KE8x?utm_source=related_read_bottom&utm_medium=article

一、概述

1.1 接口自動化概述

衆所周知,接口自動化測試有着如下特點:

 

  • 低投入,高產出。

  • 比較容易實現自動化。

  • 和 UI 自動化測試相比更加穩定。

 

如何做好一個接口自動化測試項目呢?

 

我認爲,一個“好的”自動化測試項目,需要從 “時間”、“人力”、“收益” 這三個方面出發,做好“取捨”。

 

不能由於被測系統發生一些變更,就導致花費了幾個小時的自動化腳本無法執行。同時,我們需要看到“收益”,不能爲了總想看到 100%的成功,而少做或者不做校驗,但是校驗多了維護成本一定會增多,可能每天都需要進行大量的維護。

 

所以做好這三個方面的平衡並不容易,經常能看到做自動化的同學,做到最後就本末倒置了。

 

1.2 提高 ROI

想要提高 ROI(Return On Investment,投資回報率),我們必須從兩方面入手:

 

  1. 減少投入成本。

  2. 增加使用率。

 

針對“減少投入成本”

我們需要做到:

 

  • 減少工具開發的成本。 儘可能的減少開發工具的時間、工具維護的時間,儘可能使用公司已有的,或是業界成熟的工具或組件。

  • 減少用例錄入成本。 簡化測試用例錄入的成本,儘可能多的提示,如果可以,開發一些批量生成測試用例的工具。

  • 減少用例維護成本。 減少用例維護成本,儘量只用在頁面上做簡單的輸入即可完成維護動作,而不是進行大量的代碼操作。

  • 減少用例優化成本。 當團隊做用例優化時,可以通過一些統計數據,進行有針對性、有目的性的用例優化。

 

針對“增加使用率”

我們需要做到:

 

  • 手工也能用。 不只是進行接口自動化測試,也可以完全用在手工測試上。

  • 人人能用。 每一個需要使用測試的人,包括一些非技術人員都可以使用。

  • 當工具用。 將一些接口用例當成工具使用,比如“生成訂單”工具,“查找表單數據”工具。

  • 每天測試。 進行每日構建測試。

  • 開發的在構建之後也能觸發測試。 開發將被測系統構建後,能自動觸發接口自動化測試腳本,進行測試。

 

所以,我這邊開發了 Lego 接口測試平臺,來實現我對自動測試想法的一些實踐。先簡單瀏覽一下網站,瞭解一下大概是個什麼樣的工具。

 

 

首頁

 

 

用例維護頁面

 

 

自動化用例列表

 

 

在線執行結果

 

 

用例數量統計

 

1.3 Lego 的組成

Lego 接口測試解決方案是由兩部分組成的,一個就是剛剛看到的“網站”,另一個部分就是“腳本”。

 

下面就開始進行“腳本設計”部分的介紹。

 

二、腳本設計

2.1 Lego 的做法

Lego 接口自動化測試腳本部分,使用很常見的 Jenkins+TestNG 的結構。

 

 

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 主要決定了選取哪些測試用例進行測試。

  • 一個<test>標籤,就代表一組測試,可以寫多個<test>標籤。

  • “listener”是爲了最後能夠生成一個 ReportNG 的報告。

  • Jenkins 來實現每日構建,可以使用 Maven 插件,通過命令來選擇需要執行的 XML 配置。

 

這樣做有什麼好處呢?

 

使用 SQL 最大的好處就是靈活

 

如上面的這個例子,在數據庫中會查詢出下面這 56 條測試用例,那麼這個<test>標籤就會對這 56 條用例進行逐一測試。

 

<test>標籤時,可以分組展示

 

使用多個<test>標籤來區分用例,最大的好處就是也能在最後的報告上,達到一個分組展示的效果。

 

報告更美觀豐富

 

由於使用了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 得到下表中的結果:

 

idnameagenumber
`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&lt;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 領任務,學習一些開發技巧,提高自己的代碼能力。

 

五、總結

  1. 爲了減少開發成本,使用比較常見的 Jenkins+TestNG 的腳本形式。

  2. 爲了簡化 code 操作,使用 DB 進行測試用例存儲,並抽象出用例摸版。

  3. 爲了減低新建用例成本,開發“用例維護頁面”和“一鍵生成”等工具。

  4. 爲了減低維護成本,加跳轉鏈接,維護一條用例成本在幾分鐘內。

  5. 爲了增加用例健壯性,設計了“參數化”、“前後置動作”等靈活的參數替換。

  6. 爲了易用和兼容,統一“返回結果”類型,統一“檢查點”的使用。

  7. 爲了接口自動化用例設計提供方向,結合 Jacoco 做代碼覆蓋率統計,並開發相關配置工具

  8. 爲了便於分析數據,從 DOM、CAT、Jenkins 上爬各種數據,在頁面上用圖表展示。

  9. 爲了優化用例,提供“用例打分”、“線上調用量排行”等數據進行輔助。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章