express, mocha, supertest,istanbul

引子

有羣友問到Express怎麼作 單元測試/覆蓋率測試,這是上篇所遺漏的,特此補上javascript

Express Web測試

作 Express Web 測試首先要面對的問題是在哪端進行測試:php

  • 客戶端的請求響應測試是黑盒,須要預啓動站點,且沒法附加覆蓋率測試
  • 服務端的單元測試須要 Mock ,可附加覆蓋率測試

咱們須要對Express的路由作覆蓋率測試,顯然,咱們會選擇在服務端進行測試。這意味着:每一個case須要訪問的express application 不是這樣預先啓動的:html

1
2
3
4
var  express = require( 'express' );
var  app = express();
//some router code...
app.listen(3000);

咱們須要一個工具能建立啓動express application,並 Mock 對它的請求,只有這樣,測試框架才能檢測到路由方法內部代碼執行的路徑和覆蓋率。java

這裏,咱們引入supertest 作爲 mock 工具。node

SuperTest

SuperTest 是TJ大神的又一款做品:基於SuperAgent ,提供對HTTP測試的高度抽象。所謂高度抽象的意思是:能嵌入各種測試框架,提供語義良好的斷言。git

來看段 SuperTest結合 Mocha的代碼:github

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var  app = require( '../app' );
var  request = require( 'supertest' );
 
describe( 'router testing' function  () {
     it( 'site root response' function  (done) {
         request(app)
             .get( '/' )
             .expect( 'Content-Type' 'text/html; charset=utf-8' )
             .expect(200)
             .end( function (err, res){
                 if  (err)  throw  err;
                 done();
             });
     });

簡單易懂,重點是它驅動了express。shell

測試覆蓋率

代碼覆蓋(Code coverage)是軟件測試中的一種度量,描述程式中源代碼被測試的比例和程度,所得比例稱爲代碼覆蓋率。express

如下是幾個覆蓋率指標:npm

  • 函數覆蓋率(Function coverage):調用到程式中的每個Function嗎?
  • 行覆蓋率(Line coverage):執行到程序中的每一行了嗎?
  • 語句覆蓋率(Statement coverage):若用控制流圖表示程序,執行到控制流圖中的每個節點了嗎?
  • 分支覆蓋率(Branches coverage):若用控制流圖表示程式,執行到控制流圖中的每一條邊嗎?例如控制結構中全部IF指令都有執行到邏輯運算式成立及不成立的情形嗎?
  • 條件覆蓋率(Condition coverage):也稱爲謂詞覆蓋(predicate coverage),每個邏輯運算式中的每個條件(沒法再分解的邏輯運算式)是否都有執行到成立及不成立的情形嗎?

對指標的偏好可說是見仁見智,好比大名鼎鼎的 coveralls.io 就以行覆蓋率(Line coverage) 做爲給項目頒發badge的首選指標。

咱們須要的,是一個能根據測試用例得出覆蓋率指標的工具:

Istanbul

istanbul 就是這樣一個工具,能產生 Statements/Lines/Functions/Branches 等指標報表,並以各類格式導出。

值得稱道的是,istanbul 能和 Mocha 很好的集成,如:把測試用例統一放置在 /test下,要對它們進行測試並生成覆蓋率報表,能夠在 package.json 中添加這樣的配置:

1
2
3
4
"scripts" : {
     "test" "mocha --recursive --timeout 500000 test/ --bail" ,
     "test-cov" "node node_modules/istanbul-harmony/lib/cli.js cover ./node_modules/mocha/bin/_mocha  -- --timeout 500000 --recursive test/ --bail"
}

只須要進行測試時,在項目目錄下使用命令:

1
npm test

須要進行測試並追加覆蓋率報表時,在項目目錄下使用命令:

1
npm run-script test -cov

在測試部分完成後,會獲得以下報表信息(在項目 /coverage 目錄下,會生成lcov.info 等覆蓋率數據文件:

istanbul_coverage

實例

mock 工具備了, 測試框架和覆蓋率工具也有了,就差實戰了。下面舉個粟子看看怎麼作 Express 的覆蓋率測試:

  1. 全局安裝 mocha ,istanbul 及 express
    1
    npm install  -g mocha
    1
    npm install  -g istanbul
    1
    npm install  -g express
  2. 生成一個express 站點:
    1
    express -e express-coverage
  3. 修改package.json以下,並使用npm install 安裝須要的包:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    {
       "name" "express-coverage" ,
       "version" "0.0.1" ,
       "scripts" : {
         "test" "mocha test/ --bail" ,
         "test-cov" "node node_modules/istanbul-harmony/lib/cli.js cover ./node_modules/mocha/bin/_mocha  -- test/"
       },
       "dependencies" : {
         "express" "~4.9.0" ,
         "body-parser" "~1.8.1" ,
         "cookie-parser" "~1.3.3" ,
         "morgan" "~1.3.0" ,
         "serve-favicon" "~2.1.3" ,
         "debug" "~2.0.0" ,
         "ejs" "~0.8.5" ,
         "istanbul-harmony" "*" ,
         "should" "*" ,
         "mocha" "*" ,
         "mocha-lcov-reporter" "*" ,
         "supertest"  "*"
       }
    }
  4. 把自帶的routes/index.js,、bin/www 刪除;改寫routes/users.js:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var  express = require( 'express' );
    var  router = express.Router();
    router.get( '/' function  (req, res) {
         var  msg =  'no user' ;
         res.send(msg);
    });
    router.get( '/:id' function  (req, res) {
         var  msg =  'user: '  + req.params.id;
         res.send(msg);
    });
    module.exports = router;
  5. 在項目下新建一個test目錄,放置一個 router.js,並編寫用例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    var  should = require( 'should' );
    var  app = require( '../app' );
    var  request = require( 'supertest' );
    describe( 'router testing' function  () {
         it( 'users have not id response' function  (done) {
             request(app)
                 .get( '/users' )
                 .expect( 'Content-Type' 'text/html; charset=utf-8' )
                 .expect(200)
                 .end( function (err, res){
                     if  (err)  throw  err;
                     should.exist(res.text);
                     done();
                 });
         });
     
         it( 'users have id response' function  (done) {
             request(app)
                 .get( '/users/1/' )
                 .expect( 'Content-Type' 'text/html; charset=utf-8' )
                 .expect(200)
                 .end( function (err, res){
                     if  (err)  throw  err;
                     should.exists(res.text);
                     done();
                 });
         });
    });
  6. 輸入命令npm run-script test-cov 獲得覆蓋率報表: 
    testing_coverage_first
  7. 指標有點低是否是,由於app裏有分支和代碼是用例沒跑到的: 404和500處理代碼(這些是express-generator的生成代碼:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    app.use( function (req, res, next) {
         var  err =  new  Error( 'Not Found' );
         err.status = 404;
         next(err);
    });
    app.use( function (err, req, res, next) {
         res.status(err.status || 500);
         res.render( 'error' , {
             message: err.message,
             error: {}
         });
    });
  8. 在 router.js 原有場景中,添加一個用例,加上對404代碼行的觸發:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    it( '404 response' function  (done) {
         request(app)
             .get( '/non/' )
             .expect(404)
             .end( function (err, res){
                 if  (err)  throw  err;
                 done();
             });
    });
  9. 再次輸入命令npm run-script test-cov 查看覆蓋率報表,咱們能看到進步 :) 
    testing_coverage_second

後記

badge找到合適的 Mock工具和測試框架並進行整合,Web測試及覆蓋率報表獲取的思路大抵如此。關於測試框架的各類參數組合和花樣玩法,還有不少有意思的功能(好比和 Travis-CICoveralls.io 等公共服務集成,在倉庫上展現項目狀態徽章),本文再也不贅述,有興趣的可加node學習交流羣一塊兒探討。

相關文章
相關標籤/搜索