美團接口自動化測試實踐

一、概述

1.1 接口自動化概述

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

  • 低投入,高產(chǎn)出。
  • 比較容易實現(xiàn)自動化。
  • 和UI自動化測試相比更加穩(wěn)定。

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

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

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

所以做好這三個方面的平衡并不容易,經(jīng)常能看到做自動化的同學,做到最后就本末倒置了。

1.2 提高ROI

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

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

針對“減少投入成本”

我們需要做到:

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

針對“增加使用率”

我們需要做到:

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

所以,我這邊開發(fā)了Lego接口測試平臺,來實現(xiàn)我對自動測試想法的一些實踐。先簡單瀏覽一下網(wǎng)站,了解一下大概是個什么樣的工具。

首頁

用例維護頁面

自動化用例列表

在線執(zhí)行結果

用例數(shù)量統(tǒng)計

1.3 Lego的組成

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

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

二、腳本設計

2.1 Lego的做法

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

Jenkins+TestNG的結構

相信看到這樣的模型并不陌生,因為很多的測試都是這樣的組成方式。

將自動化測試用例存儲至MySQL數(shù)據(jù)庫中,做成比較常見的“數(shù)據(jù)驅動”做法。

很多團隊也是使用這樣的結構來進行接口自動化,沿用的話,那在以后的“推廣”中,學習和遷移成本低都會比較低。

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決定了執(zhí)行什么用例, 執(zhí)行多少條用例, 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文件中。這樣做會帶來很多問題:修改測試用例需要改動大量的代碼;代碼也不便于交接給其他同學,因為每個人都有自己的編碼風格和用例設計風格,這樣交接,最后都會變成由下一個同學全部推翻重寫一遍;如果測試平臺更換,無法做用例數(shù)據(jù)的遷移,只能手動的一條條重新輸入。

所以“測試數(shù)據(jù)”與“腳本”分離是非常有必要的。

網(wǎng)上很多的范例是使用的Excel進行的數(shù)據(jù)驅動,我這里為什么改用MySQL而不使用Excel了呢?

在公司,我們的腳本和代碼都是提交至公司的Git代碼倉庫,如果使用Excel……很顯然不方便日常經(jīng)常修改測試用例的情況。使用MySQL數(shù)據(jù)庫就沒有這樣的煩惱了,由于數(shù)據(jù)與腳本的分離,只需對數(shù)據(jù)進行修改即可,腳本每次會在數(shù)據(jù)庫中讀取最新的用例數(shù)據(jù)進行測試。同時,還可以防止一些操作代碼時的誤操作。

這里再附上一段我自己寫的DataProvider_forDB方法,方便其他同學使用在自己的腳本上:

import java.sql.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * 數(shù)據(jù)源 數(shù)據(jù)庫
 *
 * @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 style="box-sizing: border-box;">標簽,就代表一組測試,可以寫多個<test style="box-sizing: border-box;">標簽。</test></test>
  • “l(fā)istener”是為了最后能夠生成一個ReportNG的報告。
  • Jenkins來實現(xiàn)每日構建,可以使用Maven插件,通過命令來選擇需要執(zhí)行的XML配置。

這樣做有什么好處呢?

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

如上面的這個例子,在數(shù)據(jù)庫中會查詢出下面這56條測試用例,那么這個<test style="box-sizing: border-box;">標簽就會對這56條用例進行逐一測試。</test>

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

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

報告更美觀豐富

由于使用了ReportNG進行報告的打印,所以報告的展示要比TestNG自帶的報告要更加美觀、并且能自定義展示樣式,點開能看到詳細的執(zhí)行過程。

如果有執(zhí)行失敗的用例,通常報錯的用例會在最上方優(yōu)先展示。

支持多團隊

當兩個團隊開始使用時,為了方便維護,將基礎部分抽出,各個團隊的腳本都依賴這個Base包,并且將Base包版本置為“SNAPSHOT版本”。使用“SNAPSHOT版本”的好處是,之后我對Lego更新,各個業(yè)務組并不需要對腳本做任何改動就能及時更新。

當更多的團隊開始使用后,比較直觀的看的話是這個樣子的:

每個團隊的腳本都依賴于我的這個Base包,所以最后,各個業(yè)務團隊的腳本就變成了下面的這個樣子:

可以看到,使用了Lego之后:

  • 沒有了Java文件,只有XML文件
  • xml中只需要配置SQL。
  • 執(zhí)行和調(diào)試也很方便。
  • 可以右鍵直接執(zhí)行想要執(zhí)行的測試配置。
  • 可以使用maven命令執(zhí)行測試:
    • mvn clean test -U -Dxml=xmlFileName 。
    • 通過參數(shù)來選擇需要執(zhí)行的xml文件。
  • 也可以使用Jenkins來實現(xiàn)定時構建測試。

由于,所有測試用例都在數(shù)據(jù)庫所以這段腳本基本不需要改動了,減少了大量的腳本代碼量。

有些同學要問,有時候編寫一條接口測試用例不只是請求一下接口就行,可能還需要寫一些數(shù)據(jù)庫操作啊,一些參數(shù)可能還得自己寫一些方法才能獲取到啊之類的,那不code怎么處理呢?

下面就進入“用例設計”,我將介紹我如何通過統(tǒng)一的用例模板來解決這些問題。

三、用例設計

3.1 一些思考

我在做接口自動化設計的時候,會思考通用、校驗、健壯、易用這幾點。

通用

  • 簡單、方便
    • 用例數(shù)據(jù)與腳本分離,簡單、方便。
    • 免去上傳腳本的動作,能避免很多不必要的錯誤和維護時間。
    • 便于維護。
  • 模板化
    • 抽象出通用的模板,可快速拓展。
    • 數(shù)據(jù)結構一致,便于批量操作。
    • 專人維護、減少多團隊間的重復開發(fā)工作。
    • 由于使用了統(tǒng)一的模板,那各組之間便可交流、學習、做有效的對比分析。
    • 如果以后這個平臺不再使用,或者有更好的平臺,可快速遷移。
  • 可統(tǒng)計、可拓展
    • 可統(tǒng)計、可開發(fā)工具;如:用例數(shù)統(tǒng)計,某服務下有多少條用例等。
    • 可開發(fā)用例維護工具。
    • 可開發(fā)批量生成工具。

校驗

在寫自動化腳本的時候,都會想“細致”,然后“寫很多”的檢查點;但當“校驗點”多的時候,又會因為很多原因造成執(zhí)行失敗。所以我們的設計,需要在保證充足的檢查點的情況下,還要盡可能減少誤報。

  • 充足的檢查點
    • 可以檢查出被測服務更多的缺陷。
  • 盡量少的誤報
    • 可以減少很多的人工檢查和維護的時間人力成本。
  • 還要
    • 簡單、易讀。
    • 最好使用一些公式就能實現(xiàn)自己想要的驗證。
    • 通用、靈活、多樣。
    • 甚至可以用在其他項目的檢查上,減少學習成本。

健壯

執(zhí)行測試的過程中,難免會報失敗,執(zhí)行失敗可能的原因有很多,簡單分為4類:

  • 被測系統(tǒng)出錯,這部分其實是我們希望看到的,因為這說明我們的自動化測試真正地發(fā)現(xiàn)了一個Bug,用例發(fā)揮了它的價值,所以,這是我們希望看到的。
  • 測試工具出錯,這部分其實是我們不希望看到的,因為很大可能我們今天的自動化相當于白跑了。
  • 測試數(shù)據(jù)錯誤,這是我們要避免的,既然數(shù)據(jù)容易失效,那我在設計測試平臺的時候,就需要考慮如果將所有的數(shù)據(jù)跑“活”,而不是只寫“死”。
  • 不可抗力,這部分是我們也很無奈的,但是這樣的情況很少發(fā)生。

那針對上面的情況:

  • 參數(shù)數(shù)據(jù)失效
    • 支持實時去數(shù)據(jù)庫查詢。
    • 支持批量查。
  • IP進場發(fā)生變更
    • 自動更新IP。
  • 靈活、可復用
    • 支持批量維護。
    • 接口測試執(zhí)行前生成一些數(shù)據(jù)。
    • 接口執(zhí)行完成后銷毀一些數(shù)據(jù)。
    • 支持參數(shù)使用另一條測試用例的返回結果。
    • 支持一些請求參數(shù)實時生成,如token等數(shù)據(jù),從而減少數(shù)據(jù)失效的問題。

通過這些手段,提高測試用例的健壯性,讓每一條自動化測試用例都能很好的完成測試任務,真正發(fā)揮出一條測試用例的價值。

易用

  • 簡單
    • 功能強大,但要人人會用。
    • 非技術人員也要會用。
  • 減少代碼操作
    • 讓自動化開發(fā)人員注意力能更多的放在用例本身,而不是浪費在無關緊要的開發(fā)工作上面。
  • 還要
    • 配置能復用。
    • 通用、易學。
    • 一些數(shù)據(jù)能自動生成。

3.2 Lego接口自動化測試用例

說了這么多,那我們來看一下一條Lego接口測試用例的樣子。

一條Lego自動用例執(zhí)行順序大概是如下圖這樣:

簡單區(qū)分一下各個部分,可以看到:

那上面圖中提到了兩個名詞:

  • “參數(shù)化”
  • “前后置動作”

下面會先對這兩個名詞做一個簡單的介紹。

3.3 參數(shù)化

比如一個請求需要用到的參數(shù)。

{
    "sync": false,
    "cityId": 1,
    "source": 0,
    "userId": 1234,
    "productId": 00004321
}

這個例子中有個參數(shù)"productId": 00004321,而由于測試的環(huán)境中,表單00004321很可能一些狀態(tài)已經(jīng)發(fā)生了改變,甚至表單已經(jīng)刪除,導致接口請求的失敗,那么這時候,就很適合對"productId": 00004321進行參數(shù)化,比如寫成這樣:

{
    "sync": false,
    "cityId": 1,
    "source": 0,
    "userId": 1234,
    "productId": ${myProductId}
}

所以對“參數(shù)化”簡單的理解就是:

通過一些操作,將一個“值”替換掉測試用例里的一個“替代字符”

${myProductId} 的值可以通過配置獲取到:

  • Key-Value
    • 配置 Value=00004321。
  • SQL獲取
    • 執(zhí)行一個select語句來實時查詢得到可用ID。
  • 已有測試用例
    • 某個接口接口測試用例的返回結果。

“參數(shù)化”實例

下面我們來看一個“參數(shù)化”的實例:

(1) 首先我們在參數(shù)化維護頁面中新建一個參數(shù)化,shopdealid。

通過配置我們可以看到這個參數(shù)的值,是執(zhí)行了一條SQL后,取用執(zhí)行結果中DealID字段的值。

(2) 在用例中,將需要這個表單號的地方用${shopdealid}替代。

那在編寫測試用例的時候,大家可以看一下這個放大的圖片,在這里的ProductID的值并不是硬代碼一個固定的表單號,而是選擇了剛才配置的參數(shù)化數(shù)據(jù)。

(3) 執(zhí)行結果中,${shopdealid} 變?yōu)閷崟r查詢數(shù)據(jù)庫的來的一個真實的表單號。

從結果中可以看到,我們的這個參數(shù)被替換成了一個有效的值,而這個值就是我們剛剛配置的那個SQL實時查詢而來的。

“參數(shù)化”的場景

多個測試用例使用同一個參數(shù)進行測試

如50條測試用例都使用同一個id作為參數(shù)進行測試,這時候我們需要變更這個id。

無參數(shù)化時:

  • 需要修改50次,即每條測試用例中的id都得進行修改。
  • 可能會有遺漏。 有參數(shù)化時:
  • ID部分用 ${myID} 替代。
  • 需要修改的話,在“參數(shù)化維護”頁面中維護 {myID}這條數(shù)據(jù)就可以。修改一次,所有使用{myID}的用例都配置完成。

測試數(shù)據(jù)過期導致測試用例執(zhí)行失敗

如一條用例參數(shù)需要傳入Token,但是Token會因為時間問題而導致過期,這時候用例就失敗了。

無參數(shù)化時:

  • 經(jīng)常修改Token,或是寫一段ID轉Token的代碼。
  • 方法可能會重復編寫。
  • 多個團隊之間可能實現(xiàn)方式也不同。

有參數(shù)化時:

  • 使用參數(shù)化工具,Lego統(tǒng)一管理。
  • 維護一個參數(shù)化 如:${測試用Token} = id:123

數(shù)據(jù)庫獲取有效測試數(shù)據(jù)

參數(shù)中需要傳入DealId作為參數(shù),寫死參數(shù)的話,如果這個DealId被修改引起失效,那這條測試用例就會執(zhí)行失敗。

不使用Lego時:

  • 測試環(huán)境中,一個訂單時常會因為測試需要被修改數(shù)據(jù),導致單號失效,最后導致自動化失敗。
  • 編寫相關代碼來做好數(shù)據(jù)準備工作。
  • 在代碼中編寫讀取數(shù)據(jù)庫的方法獲取某些內(nèi)容。

在Lego上的方案: - 使用參數(shù)化,實時獲取sql結果,查詢出一條符合條件的dealId來實現(xiàn)。 - 使用參數(shù)化,調(diào)用寫好的“生成訂單”接口用例實現(xiàn),拿單號來實現(xiàn)。 - 前后置動作,插入一條滿足條件的數(shù)據(jù)。

3.4 前后置動作

“前后置動作”的概念就比較好理解了:

在接口請求之前(或之后),執(zhí)行一些操作

目前前后置動作支持6種類型:

  • 數(shù)據(jù)庫SQL執(zhí)行
    • 有時候在執(zhí)行接口請求前,為了保證數(shù)據(jù)可用,可能需要在數(shù)據(jù)庫中插入或刪除一條信息,這時候就可以使用前后置動作里的“執(zhí)行SQL語句”類型,來編寫在接口請求前(后)的 Insert 和 Delete 語句。
  • 已有測試用例執(zhí)行
    • 比如當前測試用例的請求參數(shù),需要使用另一條測試用例的返回結果,這時候就可以使用“執(zhí)行測試用例”類型,寫上Lego上某條測試用例的ID編號,就可以在當前用例接口請求前(后)執(zhí)行這條測試用例。
    • 前后置動作中測試用例的返回結果可以用于當前用例的參數(shù),對測試用例返回結果內(nèi)容的獲取上,也支持JsonPath和正則表達式兩種方式。
  • MQ消息發(fā)送
    • 在接口請求前(后)發(fā)送MQ消息。
  • HTTP請求
  • 等待時間
  • 自定義的Java方法
    • 如果上面的方法還滿足不了需求,還可以根據(jù)自己的需要,編寫自己的Java方法。
    • 可以在Lego-Kit項目中,編寫自己需要的Java方法,選擇“執(zhí)行Java方法”,通過反射實現(xiàn)自定義Java方法的執(zhí)行。

這里的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

這樣的設計,更加幫助在用例設計時,提供數(shù)據(jù)準備的操作。

“前后置動作”實例

(1) 首先我們在前后置維護頁面中新建一個動作,獲取庫存上限未賣光團單 。

這個配置也是可以支持在線調(diào)試的,在調(diào)試中,可以看到可以使用的參數(shù)化:

(2) 在測試用例中的前置動作,添加獲取庫存上限未賣光團單 。

這樣就可以在整個測試用例中,使用${pre.ProductID},來替換掉原有的數(shù)據(jù)信息。

(3) 最后請求接口,返回了執(zhí)行成功 。

Q & A

Q:那如果同樣是獲取三個參數(shù),使用3個“參數(shù)化的Select操作”和使用1個“前置動作的Select操作”又有什么不同呢?

A: 不同在于執(zhí)行時間上。 比如,我們查詢最新的有效團單的“單號”“下單人”和“手機號”三個字段。 使用3個“參數(shù)化的Select操作”:可能當執(zhí)行{單號}的時候得到的訂單號是“10001”,但是當執(zhí)行到{下單人}的時候,可能有誰又下了一單,可能取到的下單人變成了“10002”的“李四”而不是“10001”的“張三”了,最后可能“單號”“下單人”和“手機號”三個字段去的數(shù)據(jù)并非同一行的數(shù)據(jù)。 而使用“前置動作的Select操作”:就可以避免上面的問題,因為所有字段的數(shù)據(jù)是一次性查詢出來的,就不會出現(xiàn)錯位的情況。

Q : 那“參數(shù)化的Select操作”和“前置動作的Select操作”這樣不同的取值時機又有什么好用之處呢?

A : 由于“前置動作”一定是接口請求前執(zhí)行,“參數(shù)化”一定是用到的時候才執(zhí)行這樣的特性。 所以在檢查點中,如果要驗證一個數(shù)據(jù)庫字段在經(jīng)過接口調(diào)用后發(fā)生了變更,那使用“前置動作”和“參數(shù)化”同時去查詢這個字段,然后進行比較,不一致就說明發(fā)生了變化。 所以根據(jù)使用場景,選擇合適的參數(shù)化方式,很重要,選擇對了,能大大提升測試用例的測試數(shù)據(jù)健壯性。

3.5 執(zhí)行各部分

回到一開始的流程圖,可以按照一類一類來看執(zhí)行過程。

測試發(fā)起

測試發(fā)起基本還是使用的Jenkins,穩(wěn)定、成熟、簡單、公司工具組支持,也支持從Lego的Web頁面進行執(zhí)行操作。

數(shù)據(jù) / 環(huán)境準備

使用 @DataProvider 的方式,從DB數(shù)據(jù)庫中讀取測試用例,逐一執(zhí)行進行測試。

測試執(zhí)行

在正式執(zhí)行測試用例之前,會先進行一波參數(shù)替換的動作,在調(diào)用接口之后,還會執(zhí)行一次參數(shù)替換動作。

參數(shù)替換后會進行前置動作的執(zhí)行,然后在調(diào)用接口之后還會執(zhí)行測試后動作,最后執(zhí)行后置動作。

接口請求這部分就沒什么好說的了,就是通過接口請求的參數(shù),請求對應的接口,拿到返回結果。

這里的話是為了方便通用,所以要求返回的結果都是使用的String類型。這樣做最大的好處就是。比如說我現(xiàn)在有一種新的接口類型需要接入。那只需要寫一個方法能夠請求到這個接口,并且拿到String類型的返回結果,就可以很快將新的接口類型接入Lego測試平臺進行接口測試。

檢查點校驗

檢查點部分是一條自動化測試用例的精髓,一條自動化測試用例是否能真正的發(fā)揮它的測試功能,就是看QA對這條測試用例的檢查點編寫是否做了良好設計。在Lego平臺上,目前我擁有的檢查點有6種不同的類型。

  • 異常檢查點
    • 當返回結果為異常時,則會報錯。
    • 但是有時候為了做異常測試,可以將這個檢查點關掉。
  • 不為空檢查點
    • 顧名思義,當出現(xiàn)”“、”[]“、”{}“、null 這樣的的結果,都會報錯。也可以根據(jù)自己用例的實際情況關閉。
  • 包含檢查點
  • 不包含檢查點
    • “包含”和“不包含”檢查點是將接口的返回結果作為一個String類型來看,檢查所有返回內(nèi)容中是否“包含”或“不包含”指定的內(nèi)容。
  • 數(shù)據(jù)庫參數(shù)檢查點
    • 顧名思義,不做過多的解釋了。
  • JsonPath檢查點
    • 這是我在Lego上設計的最具有特色的一種檢查點類型。

JsonPath的基本寫法是{JsonPath語法}==value

JsonPath的語法和XPath的語法差不多,都是根據(jù)路徑的方法找值。這里也是主要是針對返回結果為JSON數(shù)據(jù)的結果,進行檢查。

說完了“JsonPath的語法”,現(xiàn)在說一下“JsonPath檢查點的語法”,“JsonPath檢查點的語法”是我自己想的,主要針對以下幾種數(shù)據(jù)類型進行校驗:

(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) 數(shù)值校驗

  • 等于:=
  • 大于:>
  • 大于等于:>=
  • 小于:<
  • 小于等于:<=

例如:

  • {$.[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個內(nèi)容是否等于222。
  • {$..value}.get(2)='22':檢查返回的JSON中每個JSON的value字段的列表中第3個內(nèi)容是否包含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(chǎn)’, ‘b’] [[‘a(chǎn)’, ‘b’],[‘a(chǎn)’, ‘b’],[‘a(chǎn)’, ‘b’, ‘c’]] [‘a(chǎn)’, ‘b’] 作為3個檢查點,道理和1一樣,列表中的數(shù)據(jù)分別和期望值做比較。
除此之外,還有非常多的花樣玩法

JsonPath中的檢查支持“參數(shù)化”和“前后置動作”,所以會看到很多如:

{.param}=‘{param}’ && {.param}=={pre.param}

這樣的檢查點:

“參數(shù)化”和“前后置動作”也支持遞歸配置,這些都是為了能夠讓接口自動化測試用例寫的更加靈活好用。

測試結果

image.png

使用ReportNG可以打印出很漂亮的報告。

報告會自定義一些高亮等展示方式,只需要在ReportNG使用前加上下面的語句,就可以支持“輸出逃逸”,可使用HTML標簽自定義輸出樣式。

System.setProperty("org.uncommons.reportng.escape-output", "false");

后期優(yōu)化

當使用Jenkins執(zhí)行后,通過Jenkins API 、和Base包中的一些方法,定時獲取測試結果,落數(shù)據(jù)庫,提供生成統(tǒng)計圖表用。

四、網(wǎng)站功能

4.1 站點開發(fā)

既然打算做工具平臺了,就得設計方方面面,可惜人手和時間上的不足,只能我一人利用下班時間進行開發(fā)。也算是擔任了Lego平臺的產(chǎn)品、后端開發(fā)、前端開發(fā)、運維和測試等各種角色。

Jenkins+TestNG+ReportNG+我自己開發(fā)的基本接口自動化測試Base jar包,基本上沒什么太大難度。但是站點這塊,在來美團之前,還真沒開發(fā)過這樣的工具平臺,這個算是我的第一個帶Web界面的工具。邊Google邊做,沒想到不久還真的架起來了一個簡易版本。

使用 Servlet + Jsp 進行開發(fā),前端框架使用Bootstrap,前端數(shù)據(jù)使用jstl,數(shù)據(jù)庫使用MySQL,服務器使用的公司的一臺Beta環(huán)境Docker虛擬機,域名是申請的公司內(nèi)網(wǎng)域名,并開通北京上海兩側內(nèi)網(wǎng)訪問權限。

功能上基本都是要滿足的,界面上,雖然做不到驚艷吧,但是絕對不能丑,功能滿足,但是長得一副80年代的界面,我自己都會嫌棄去使用它,所以界面上我還是花了一些時間去調(diào)整和設計。熟練以后就快多了。

4.2 整體組成

目前Lego由五個不同的項目組成,分別是“測試腳本”、“Lego-web頁面項目”、“用于執(zhí)行接口測試的base包”、“小工具集合Lego-kit”和“l(fā)ego-job”,通過上圖可以看出各項目間的依賴關系。

細化各個項目的功能,就是下圖:

簡單來說,網(wǎng)站部分和腳本是分離的,中間的紐帶是數(shù)據(jù)庫。所以,沒有網(wǎng)站,腳本執(zhí)行一點問題也沒有;同樣的,網(wǎng)站的操作,和腳本也沒有關系。

4.3 使用-日常維護

Step 1

每天上班來會收到這樣的測試郵件,通過郵件能知道昨晚執(zhí)行的情況。如果有報錯,可以點擊“詳細報告鏈接”,跳轉到在線報告。

Step 2

在現(xiàn)報告可以直接看到執(zhí)行報錯的信息,然后點擊“LEGO維護傳送門”,可以跳轉到Lego站點上,進行用例維護。

Step 3

跳轉到站點上以后,可以直接展示出該條測試用例的所有信息。定位,維護、保存,維護用例,可以點擊“執(zhí)行”查看維護后的執(zhí)行結果,維護好后“保存”即可。

僅僅3步,1~2分鐘即可完成對一條執(zhí)行失敗的用例進行定位、調(diào)試和維護動作。

4.4 用例編輯

通過頁面,我們就可以對一條測試用例進行:

  • 新建
  • 復制
  • 編輯
  • 刪除
  • 是否放入每日構建中進行測試

4.5 在線調(diào)試

lego-web項目同樣的使用base進行的用例執(zhí)行,所以執(zhí)行結果和打印都與腳本執(zhí)行的一致的。

4.6 用例生成工具

為了更方便的寫用例,針對部分接口開發(fā)了一鍵批量生成用例的小工具。

4.7 執(zhí)行結果分析

通過Jenkins接口、Base包中基礎Test方法,將結果收集到數(shù)據(jù)庫,便于各組對測試結果進行分析。

這是每天執(zhí)行后成功率走勢圖:

也可以按月進行統(tǒng)計,生成統(tǒng)計的圖表,幫助各個團隊進行月報數(shù)據(jù)收集和統(tǒng)計。

4.8 失敗原因跟蹤

有了能直觀看到測試結果的圖表,就會想要跟蹤失敗原因。

所以在成功率數(shù)據(jù)的右邊,會有這樣的跟蹤失敗原因的入口,也可以很直觀地看到哪一些失敗的原因還沒有被跟蹤。點開后可以對失敗原因進行記錄。

最后會有生成圖表,可以很清晰地看到失敗原因以及失敗類型的占比。

4.9 代碼覆蓋率分析

結合Jacoco,我們可以對接口自動化的代碼覆蓋率進行分析。

在多臺Slave機器上配置Jacoco還是比較復雜的,所以可以開發(fā)覆蓋率配置輔助工具來幫助測試同學,提高效率。

4.10 用例優(yōu)化方向

除了上面的圖表,還會給用例優(yōu)化提供方向。

通過用例數(shù)量統(tǒng)計的圖表,我們可以知道哪些服務用例還比較少,哪些環(huán)境的用例還比較少,可以比較有針對性的進行測試用例的補充。

通過失敗原因的圖表,我們可以改善自己用例中的“參數(shù)化”和“前后置動作”的使用,增加測試用例的健壯性。

通過線上接口調(diào)用量排序的圖表。我們可以有效的知道優(yōu)先維護哪些服務的測試用例,通過表格中,我們可以看到,哪些服務已經(jīng)覆蓋了測試用例,哪些沒有被覆蓋, 給各組的QA制定用例開發(fā)計劃,提供參考。

同時在維護接口自動化測試的時候,都會看到用例評分的情況,來協(xié)助QA提高用例編寫的質(zhì)量。

4.11 收集反饋/學習

還做了“需求白板”,用來收集使用者的需求和Bug。除此之外,Lego平臺已經(jīng)不只是一個接口測試的平臺,還可以讓想學習開發(fā)的QA領任務,學習一些開發(fā)技巧,提高自己的代碼能力。

五、總結

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

推薦

年薪百萬的測開也得懂的接口測試!你有什么理由可以不學習?https://www.bilibili.com/video/BV1jp4y1P7KL

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容