1. 程式人生 > >美團接口自動化測試實踐

美團接口自動化測試實踐

接口測試 生成 [] mage except 一個數據庫 分享 同一行 解釋

一、概述

1.1 接口自動化概述

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

  • 低投入,高產出。
  • 比較容易實現自動化。
  • 和UI自動化測試相比更加穩定。

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

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

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

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

1.2 提高ROI

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

  1. 減少投入成本。
  2. 增加使用率。

針對“減少投入成本”

我們需要做到:

  • 減少工具開發的成本。盡可能的減少開發工具的時間、工具維護的時間,盡可能使用公司已有的,或是業界成熟的工具或組件。
  • 減少用例錄入成本。簡化測試用例錄入的成本,盡可能多的提示,如果可以,開發一些批量生成測試用例的工具。
  • 減少用例維護成本。減少用例維護成本,盡量只用在頁面上做簡單的輸入即可完成維護動作,而不是進行大量的代碼操作。
  • 減少用例優化成本。當團隊做用例優化時,可以通過一些統計數據,進行有針對性、有目的性的用例優化。

針對“增加使用率”

我們需要做到:

  • 手工也能用。不只是進行接口自動化測試,也可以完全用在手工測試上。
  • 人人能用。每一個需要使用測試的人,包括一些非技術人員都可以使用。
  • 當工具用。將一些接口用例當成工具使用,比如“生成訂單”工具,“查找表單數據”工具。
  • 每天測試。進行每日構建測試。
  • 開發的在構建之後也能觸發測試。開發將被測系統構建後,能自動觸發接口自動化測試腳本,進行測試。

所以,我這邊開發了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領任務,學習一些開發技巧,提高自己的代碼能力。

五、總結

  1. 為了減少開發成本,使用比較常見的Jenkins+TestNG的腳本形式。
  2. 為了簡化code操作,使用DB進行測試用例存儲,並抽象出用例摸版。
  3. 為了減低新建用例成本,開發“用例維護頁面”和“一鍵生成”等工具。
  4. 為了減低維護成本,加跳轉鏈接,維護一條用例成本在幾分鐘內。
  5. 為了增加用例健壯性,設計了“參數化”、“前後置動作”等靈活的參數替換。
  6. 為了易用和兼容,統一“返回結果”類型,統一“檢查點”的使用。
  7. 為了接口自動化用例設計提供方向,結合Jacoco做代碼覆蓋率統計,並開發相關配置工具
  8. 為了便於分析數據,從DOM、CAT、Jenkins上爬各種數據,在頁面上用圖表展示。
  9. 為了優化用例,提供“用例打分”、“線上調用量排行”等數據進行輔助。



美團接口自動化測試實踐