Nodejs測試:從0到90(理論篇)

簡介: 最近在負責一個基於nodejs的應用,在不少方面都經歷了一個從無到有的過程,測試也是如此。剛開始時,代碼都寫很差,更別提測試了,那時測試爲0。經歷過一段時間後, 測試覆蓋率在90%+。這就是個人從0到90。剩下的10,還有很長的路,且待下回分解。javascript

最近在負責一個基於nodejs的應用,在不少方面都經歷了一個從無到有的過程,測試也是如此。剛開始時,代碼都寫很差,更別提測試了,那時測試爲0。經歷過一段時間後,尤爲是看到 npm 上優秀的庫的測試覆蓋率都在100%時,痛下決心開始學習 nodejs 的測試, 目前這個應用的測試覆蓋率在90%+。這就是個人從0到90。剩下的10,還有很長的路,且待下回分解。html

寫在前面的

對於開發者,測試的重要性毋庸置疑, 所謂」無測試不上線「,」無測試不重構「等。但在實踐的過程當中,測試老是有這樣的那樣的問題,隨便列舉下:java

  • 不寫測試。通常是以爲寫測試浪費時間,也有多是不會寫;
  • 亂寫測試。隨意的寫,沒有規範,沒有覆蓋率,沒有集成;
  • 寫了跟沒寫同樣。這樣的測試一般是不可讀、不可維護、不可信任,不可重複或獨立運行(強依賴某些特定的環境或條件)、不執行(一般只是開發的時候執行一下,以後不再會執行,或者執行太慢,沒有人願意執行)。

一個好的測試

一千我的眼中有至少二千種測試理念或方法,很難定義什麼是一種好的測試。在團隊一直負責測試的推動,咱們的理念很簡單,包括如下幾個原則:node

  • 覆蓋75%+

一個好的測試,最重要的衡量標準就是測了多少(覆蓋率),75%是最低的標準。這個標準對java來講基本可行,但對nodejs不太適用,javascript是弱類型的、動態語言,沒有編譯階段,不少錯誤只能在運行時纔會被發現,因此須要更高的覆蓋率,最好是100%,目前我的的標準是90%+。git

  • 可重複執行

每個測試用例,不管在任何環境下,都應該能夠反覆執行,併產生相同的結果。只有這樣,纔可以相任你的測試,進而發現真正的bug。這也是集成測試最低要求。github

  • 保持獨立

一個測試用例只測試代碼的某一方面,如一個分支,且不強依賴某些特定的環境或條件。npm

  • 可讀、可維護、可信任
  • 快快快

不管是單個測試用例,仍是集成測試,必需要保證測試執行足夠的快。json

測什麼

測試測什麼主要依據具體的需求、業務、成本、語言等,但也有必定的共性,單元測試準則 給出了一些準則可供參考,這裏再也不討論。小程序

怎麼測

又是一個很是大的話題,本文僅從我的角度給出nodejs測試的工具及方法。瀏覽器

概述

框架

Nodejs 測試框架衆多,目前使用最廣的是Mocha。本文詳細說明下 Mocha, 並簡要介紹幾種其它框架。本文的示例,如無特別說明,都是基於Mocha.

Mocha

mocha_logo

A simple, flexible, fun JavaScript test framework for node.js and the browser.

Mocha 是一個功能豐富的Javascript測試框架,它能運行在Node.js和瀏覽器中,支持BDD、TDD式的測試。

快速開始

  • 安裝

    npm install -g mocha

  • 寫一個簡的測試用例

    var assert = require('chai').assert; describe('Array', function() { describe('#indexOf()', function () { it('should return -1 when the value is not present', function () { assert.equal(-1, [1,2,3].indexOf(5)); assert.equal(-1, [1,2,3].indexOf(0)); }); }); });

  • 運行

    $ mocha

輸出結果以下:

.

   1 test complete (1ms)
複製代碼

使用

斷言

Mocha 容許你使用任何你想用的斷言庫,包括:

  • should.js BDD style shown throughout these docs
  • expect.js expect() style assertions
  • chai expect(), assert() and should style assertions
  • better-assert c-style self-documenting assert()
  • unexpected the extensible BDD assertion toolkit
鉤子 Hooks

Mocha 提供了 before(), after(), beforeEach(), afterEach()等 hooks,用於設置前置條件及清理測試,示例以下:

describe('hooks', function() {
  before(function() {
    // runs before all tests in this block
  })
  after(function(){
    // runs after all tests in this block
  })
  beforeEach(function(){
    // runs before each test in this block
  })
  afterEach(function(){
    // runs after each test in this block
  })
  // test cases
})
複製代碼
專用測試 或 跳過測試

專用測試容許只測試指定的測試集或用例,只需在測試集或用例前加.only(),示例以下:

describe('Array', function(){
  describe.only('#indexOf()', function(){
    ...
  })
})
複製代碼

跳過相似於 junit 的 @Ignore, 用於跳過或忽略指定的測試集或用例,只需在測試集或用例前加.skip(),示例以下:

describe('Array', function(){
  describe.skip('#indexOf()', function(){
    ...
  })
})
複製代碼
編輯器插件

除了使用 Mocha 提供的命令行外,還可使用 編輯器插件 運行,目前支持了:

  • TextMate
  • JetBrains
  • Wallaby.js
  • Emacs

以JetBrains爲例,JetBrain 爲其 IDE 套件(IntelliJ IDEA, WebStorm等)提供了 NodeJS, 能夠直接運行或調試 mocha 測試用例。基本步驟:

  • 安裝 NodeJS 插件(若是沒有安裝的話,默認IntelliJ IDEA, WebStorm都已安裝):經過 Preferences > Plugins 查找名爲 NodeJS 的插件並安裝;
  • 添加 mocha 測試。經過 Edit Configuration 添加 Mocha, 示例以下:

add_mocha_test

  • 運行或調試。支持按目錄、文件、測試集、測試用例等運行或調試,運行示例以下:

run_mocha_test

其它

如下列舉幾個基於 nodejs 的測試框架或工具,它們可用於 nodejs 或 瀏覽器端javascript 代碼的測試,不在本文討論範圍,詳見官方文檔。

Jasmine

jasmine_logo

A Behavior Driven Development JavaScript testing framework

Jasmine is a behavior-driven development framework for testing JavaScript code. It does not depend on any other JavaScript frameworks. It does not require a DOM. And it has a clean, obvious syntax so that you can easily write tests.

更多見 官方文檔.

Karma Runner

karma_logo

To bring a productive testing environment to developers

The main goal for Karma is to bring a productive testing environment to developers. The environment being one where they don't have to set up loads of configurations, but rather a place where developers can just write the code and get instant feedback from their tests.

更多見 官方文檔.

工具

mocha 提供了測試的基本框架,在特定的場景下的測試還須要其它工具作以輔助,這個列舉幾個經常使用的工具。

SuperTest

SuperTest 提供對 HTTP 測試的高度抽象, 極大地簡化了基於 HTTP 的測試。

The motivation with this module is to provide a high-level abstraction for testing HTTP, while still allowing you to drop down to the lower-level API provided by super-agent.

安裝
$  npm install supertest --save-dev
複製代碼
使用示例
  • 簡單的 HTTP 請求

    var request = require('supertest');

    describe('GET /user', function() { it('respond with json', function(done) { request(app) .get('/user') .set('Accept', 'application/json') .expect('Content-Type', /json/) .expect(200, done); }); });

  • 上傳文件

    request(app) .post('/') .field('name', 'my awesome avatar') .attach('avatar', 'test/fixtures/homeboy.jpg') // ..

  • 修改響應頭或體

    describe('GET /user', function() { it('user.name should be an case-insensitive match for "tobi"', function(done) { request(app) .get('/user') .set('Accept', 'application/json') .expect(function(res) { res.body.id = 'some fixed id'; res.body.name = res.body.name.toUpperCase(); }) .expect(200, { id: 'some fixed id', name: 'TOBI' }, done); }); });

更多見 文檔

Istanbul

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

  • 行覆蓋率(line coverage):是否每一行都執行了
  • 函數覆蓋率(function coverage):是否每一個函數都調用了
  • 分支覆蓋率(branch coverage):是否每一個if代碼塊都執行了
  • 語句覆蓋率(statement coverage):是否每一個語句都執行了

Istanbul 是 JavaScript 語言最流行的代碼覆蓋率工具。

快速開始
  • 安裝

    $ npm install -g istanbul

  • 運行

最簡單的方式:

$ cd /path/to/your/source/root
$ istanbul cover test.js
複製代碼

輸出運行結果:

..
  test/app/util/result.test.js
     should static create
     should be success
     should be static success
     should be error
     should be static error

  299 passing (13s)

[mochawesome] Report saved to /opt/source/node_modules/.mochawesome-reports/index.html

=============================== Coverage summary ===============================
Statements   : 92.9% ( 1505/1620 )
Branches     : 85.42% ( 410/480 )
Functions    : 94.33% ( 133/141 )
Lines        : 93.01% ( 1504/1617 )
================================================================================
Done
複製代碼

這條命令同時還生成了一個 coverage 子目錄,其中的 coverage.json 文件包含覆蓋率的原始數據,coverage/lcov-report 是能夠在瀏覽器打開的覆蓋率報告,以下圖所示:

icov

模式

常見的測試模式包括TDD, BDD。

TDD (Test Driven Development)

測試驅動開發是敏捷開發中的一項核心實踐和技術,也是一種設計方法論。TDD的原理是在開發功能代碼以前,先編寫單元測試用例代碼,測試代碼肯定須要編寫什麼產品代碼。TDD的基本思路就是經過測試來推進整個開發的進行,但測試驅動開發並不僅是單純的測試工做,而是把需求分析,設計,質量控制量化的過程。總體流程以下:

tdd_flowchart

以一個階乘的小程序爲例。先寫出的測試用例,以下所示。此時運行確定會報錯,由於被測試的程序尚未寫。

var assert = require('assert'),
    factorial = require('../index');

suite('Test', function (){
    suite('#factorial()', function (){
        test('equals 1 for sets of zero length', function (){
            assert.equal(1, factorial(0));
        });

        test('equals 1 for sets of length one', function (){
            assert.equal(1, factorial(1));
        });

        test('equals 2 for sets of length two', function (){
            assert.equal(2, factorial(2));
        });

        test('equals 6 for sets of length three', function (){
            assert.equal(6, factorial(3));
        });
    });
});
複製代碼

開始寫階乘的邏輯,以下所示。

module.exports = function (n) {
    if (n < 0) return NaN;
    if (n === 0) return 1;

    return n * factorial(n - 1);
};
複製代碼

此時再運行以前的測試用例,看其是否經過,若是所有經過則表示開發完成,如不經過則反覆修改被測試的邏輯,直到所有的經過爲止。

BDD (Behavior Driven Development)

行爲驅動開發是一種敏捷軟件開發的技術,它鼓勵軟件項目中的開發者、QA和非技術人員或商業參與者之間的協做。主要是從用戶的需求出發,強調系統行爲。其最顯著的特徵是,經過編寫行爲和規格說明來驅動軟件開發。行爲和規格說明看上去與測試十分類似,但它們以前仍是有顯著的不一樣。

仍是以上面的階乘爲例,BDD模式的測試用例以下:

var assert = require('assert'),
    factorial = require('../index');

describe('Test', function (){
    before(function(){
        // Stuff to do before the tests, like imports, what not
    });

    describe('#factorial()', function (){
        it('should return 1 when given 0', function (){
            factorial(0).should.equal(1);
        });

        it('should return 1 when given 1', function (){
            factorial(1).should.equal(1);
        });

        it('should return 2 when given 2', function (){
            factorial(2).should.equal(2);
        });

        it('should return 6 when given 3', function (){
            factorial(3).should.equal(6);
        });
    });

    after(function () {
        // Anything after the tests have finished
    });
});
複製代碼

從示例上看,BDD的測試用例與TDD最大的不一樣在於其措辭,BDD讀起來更像是一個句子。所以BDD的測試用例可做爲開發者、QA和非技術人員或商業參與者之間的協同工具,對開發者來講,若是你能夠很是流暢地讀測試用例,天然可以寫出更好、更全面的測試。

TDD vs BDD

TDD和BDD本質和目標都是一致的。只是在實施方法上,進行了不一樣的探討來完善整個敏捷開發的體系。TDD的迭代反覆驗證是敏捷開發的保障,但沒有明確如何根據設計產生測試,並保障測試用例的質量,而BDD倡導你們都用簡潔的天然語言描述系統行爲的理念,剛好彌補了測試用例(即系統行爲)的準確性。

幾乎全部基於Nodejs的庫或應用都選擇了BDD, 至於爲何,我尚未搞明白。

斷言

目前比較流行的斷言庫包括:

  • should.js BDD style shown throughout these docs
  • expect.js expect() style assertions
  • chai expect(), assert() and should style assertions
  • better-assert c-style self-documenting assert()
  • unexpected the extensible BDD assertion toolkit

這些斷言庫除了風格上略有不一樣外,實如今功能幾乎徹底一致,可根據我的喜愛或應用需求選擇。

相關文章
相關標籤/搜索