測試來帶個節(jié)奏之API測試工具篇

項目組里面的e2e測試運行多年,歷經(jīng)了經(jīng)常會出現(xiàn)各種莫名其妙的環(huán)境問題、運行變慢等問題后,項目組終于決定引入API功能測試。同時可以在盡量保證測試覆蓋率的前提下把重復測試的e2e測試腳本清理掉,提高持續(xù)集成效率(策略參考測試金字塔)。

那么問題來了,做API功能測試如何選擇工具勒?API功能測試可以通過soapUI或者postman等帶GUI的工具簡單錄制腳本執(zhí)行,也可以通過開源項目工具自己寫代碼完成。根據(jù)項目的實際情況,這里我們選擇使用后者,便于定制和持續(xù)集成。

工具選擇

目前市面上比較流行的API測試開源框架有很多。首先能夠想到的就是REST-assured。Rest-Assured 是一套由 Java 實現(xiàn)的 REST API 測試框架,它是一個輕量級的 REST API 客戶端,可以直接編寫代碼向服務器端發(fā)起 HTTP 請求,并驗證返回結(jié)果。官方的介紹是:

Testing and validation of REST services in Java is harder than in dynamic languages such as Ruby and Groovy. REST Assured brings the simplicity of using these languages into the Java domain.

打開github提交記錄,發(fā)現(xiàn)這個框架最近還有人在持續(xù)提交代碼,說明維護的還不錯,列為備選項目。

另外經(jīng)過各種途徑了解到目前還有一套非常流行的,由大神tj等人開發(fā)的nodeJS測試框架supertest。這是一套脫胎于著名的superagent的API測試框架,官方的說法是:

  • Super-agent driven library for testing node.js HTTP servers using a fluent API

稍微對比一下這兩個工具,從幾個方面來考慮取舍:

  1. 項目代碼基于Java,同時也有NodeJS代碼在里面,從環(huán)境上來講兩個工具都不需要再額外配置。這點兩者打個平手。
  2. 學習成本方面,兩個工具都可以方便的從網(wǎng)上搜出一大堆學習資料,而且官方給的資料也比較全。又是平手。
  3. 維護成本上講,supertest是基于動態(tài)語言,不需要浪費編譯時間;萬一寫錯了代碼立馬改完立馬重新跑起來。而且官網(wǎng)上號稱"SuperTest works with any test framework"可擴展性貌似也比較強。
  4. 從可移植性上看,supertest由于使用nodeJS,理論上只要框架做的夠好,只要有node,就可以把同一套腳本丟到各種不同的地方運行。
  5. 最后再對比下易用性。安裝方面,REST-assured通常會借助如maven、grade之類的工具安裝,配置運行環(huán)境比較麻煩。而superset只需要簡單的一行npm install 命令安裝后即可使用??紤]到我比較懶,supertest完勝,就醬。

開始入坑

開始學習supertest。
首先打開它的github,了解supertest幾個關(guān)鍵信息:

  • 繼承了superagent所有的API和用法。
  • 使用前需安裝node,然后用npm install supertest --save-dev或者cnpm install supertest --save-dev安裝supertest。
  • 和superagent一樣,需要通過調(diào)用.end() 執(zhí)行一個request請求。
  • 調(diào)用.expect()來做斷言,如果在里面填入數(shù)字,默認是檢查http請求返回的狀態(tài)碼;

完了我們來分析下官方示例代碼,然后仿造它來擼一段代碼試試看。

var request = require('supertest');
var express = require('express');
var app = express();

這里的app目測只是用來做一個mock server,跟supertest有關(guān)的測試只有下面這部分

request(app) 
    .get('/user') 
    .expect('Content-Type', /json/) 
    .expect('Content-Length', '15') 
    .expect(200) 
    .end(function(err, res) { 
        if (err) throw err; 
    });

分析這段測試代碼,首先是用request(app)實例化一個server,然后是.expect()分別驗證了response header里面的content-type,content-length和response的http status是否200. 這就是supertest的基本寫法了。

小試牛刀

我們用全球最大的同性交友平臺github來做個實驗,設計一個判斷是否成功進入首頁的用例。

準備工作:使用你的chrome,打開develop tools的Network標簽,先看看進入github首頁時上有哪些請求,記錄下進入首頁的請求,找到這個請求的URL,Method等關(guān)鍵信息。

請求

實施階段:我們再隨便打開個vim,記事本什么的文本編輯工具擼一小段代碼試試刀:

var request = require('supertest')('https://github.com/');

request
    .get('/')
    .expect(2010)
    .end(function(err, res) {
            if (err) throw err;
    });

保存下來,命名個test.js什么的,然后運行它

  node test.js

然后你發(fā)現(xiàn)得到這個提示異常的結(jié)果

執(zhí)行結(jié)果

這說明我們的斷言成功了!把.expect(2010)改成實際會返回的.expect(200)再試試看,沒有返回異常結(jié)果說明測試通過了!

優(yōu)化一下:雖然測試成功了,但是這個測試結(jié)果的可讀性實在是有些令人不甚滿意,尤其是測試成功了連個提示都沒有。

于是我們考慮用官網(wǎng)例子中提到的測試框架Mocha來優(yōu)化下這個測試。

Mocha是一個優(yōu)秀的JavaScript測試框架,長得跟Jasmine一個樣。官網(wǎng)上的介紹是:

Mocha is a feature-rich JavaScript test framework running on Node.js and in the browser, making asynchronous testing simple and fun. Mocha tests run serially, allowing for flexible and accurate reporting, while mapping uncaught exceptions to the correct test cases. Hosted on GitHub.

這個框架提供了各種style的測試報告。結(jié)合supertest使用,可以讓我們的API測試報告可視化上一個檔次。

順便可以加上個常用的post請求的測試:

var request = require('supertest')('https://github.com');

describe('Github home page',function(){

  this.timeout(10000);

  before('must be on home page',function(done){
    request.get('/')
      .expect(200,done);
  });

  it('could be navigated to register page',function(done){
    request.get('/join')
      .expect(200,done);
  });

  it('will refuse the request if username has been taken',function(done){
    request.post('/signup_check/username')
      .type('form')
      .send('value=lala')
      .expect(404)
      .end(function(err,res){
        if (err) return done(err)
        done();
      })
  });
});

這個測試比起剛才的版本更加具有可讀性,借助Mocha框架,每段測試之前都有一個描述信息,一看就知道你這段代碼在測試什么。

其中before()是Mocha提供的hook,相當于beforeAll,會在所有測試前執(zhí)行。其他hook還有會在所有測試執(zhí)行之后執(zhí)行的after(),會在每個測試前都執(zhí)行一遍的beforeEach()和會在每一個測試執(zhí)行之后都執(zhí)行一遍的afterEach()。hook用在清理測試數(shù)據(jù)方面會非常方便。

然后describe()描述了是測試的是什么東西:

describe('描述測試對象',function(){
  //測試用例
})

describe()里面的it()則描述了具體的測試用例:

it('描述測試用例', function(done){
  //測試用例實現(xiàn)
  done();
})

done()是Mocha提供的回調(diào)方法,如果沒有done()的話Javascript回一直等待回調(diào)致超時。順帶提一下Mocha的默認超時時間是2秒,所以在describe的下面加上this.timeout(10000);把超時時間重新設置為10秒。

需要注意的是在雖然使用Mocha的時候可以忽略superset的.end(),而直接在.expect()添加done參數(shù),例如.expect(200,done)。但是如果使用了.end()的寫法的話,仍然需要在.end()塊兒中調(diào)用done()。

最后個用例中的.send('value=lala')是post的request body,通過.type()來指定類型。.type()在缺省狀態(tài)下默認是JSON(詳見superagent源代碼),本例中使用的是form類型。 當然,也可以不用send()而是選擇直接在post的url中加上參數(shù)request.post('/signup_check/username?value=lala'),但是如果要參數(shù)化的話,還是推薦用.send()。

Mocha還提供了watch功能,使用帶參數(shù)的命令mocha -w 測試腳本.js來監(jiān)視測試腳本,當腳本有變化的時候Mocha會自動運行腳本。

測試結(jié)果如下:

測試不通過

更新最后一個用例中的.expect(404).expect(403),測試通過。

測試通過

現(xiàn)在不管是測試代碼的可讀性還是測試報告的可讀性,都比之前強多了。而且還可以使用--reporter參數(shù)讓測試報告變成各種形狀,比如

nyan

查漏補缺:總算是解決了代碼可讀性和測試報告的問題。再回過頭來看看整個demo,突然發(fā)現(xiàn)調(diào)研了這么半天,竟然忽略了在很多業(yè)務場景中,調(diào)用API需要驗證用戶是否登錄的問題。換句話說,需要在不同的http請求中保持cookie。

幸好supertest提供了這個解決方案,使用supertest的agent功能來解決這個問題。

var request = require('superset')

describe('測試cookie', function(){

  var agent = request.agent('待測server');

  it('should save cookies', function(done){
    agent
    .get('/')
    .expect('set-cookie', 'cookie=hey; Path=/', done);
  })

  it('should send cookies', function(done){
    agent
    .get('/return')
    .expect('hey', done);
  })
})

可以看到第一個用例是測試cookie=hey,而到了第二個測試里面,由于被測實例由單純的"request"變成了"request.agent()",所以cookie “hey”被agent帶入到了第二個用例中,當訪問"/return"的時候不用再重新set cookies了。

另外我們也可以通過在每次請求前去set cookie的方法達到同樣的效果。

.set('Cookie', 'a cookie string') 

最后如果是要測試授權(quán)資源的話,superagent也提供了.auth()方法去獲取授權(quán)。

request .get('http://local') 
    .auth('tobi', 'learnboost') .
end(callback);

現(xiàn)在看上去調(diào)研工作算是差不多了,能夠滿足大部分的測試場景。接下來只需要再設計下測試代碼結(jié)構(gòu),抽象下公共組件,做下參數(shù)化,分離下測試數(shù)據(jù)就搞定了??墒羌毾胂?,如果需要寫了一大堆測試的話,難道要挨個去執(zhí)行mocha xxx腳本的命令來跑測試?

還好項組目已經(jīng)在用grunt構(gòu)建工具。谷歌一下發(fā)現(xiàn)有一個grunt插件“grunt-mocha-test”貌似挺不錯的。按照它的說明文檔,只需要在grunt配置文件里面加上一段

grunt-mocha-test配置

其中reporter就是制定報告的格式, src就是需要執(zhí)行的腳本的路徑,*.js指定執(zhí)行全部js格式的文件。

最后再注冊一個grunt命令,比如:

grunt.registerTask('apitest', 'mochaTest');

就能簡單的在命令行中使用

grunt apitest

來執(zhí)行所有的測試文件了。這樣也可以方便的在Jenkins中配置一個新的測試任務,加入持續(xù)集成。

至此,工具選型全部完成,核心是supertest,包裝是mocha,執(zhí)行用grunt,收工。

總結(jié)一下

總結(jié)一下,在工具選型的時候,建議考慮這些方面:

  • 結(jié)合項目技術(shù)棧使用
  • 新工具學習成本、維護成本、可擴展性
  • 是否可以簡單實現(xiàn)代碼滿足所有業(yè)務場景,比如非REST風格的API,或者一些特殊場景
  • 代碼易讀,測試報告可視化
  • 腳本執(zhí)行簡單
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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

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