簡介: 最近在負責一個基於nodejs的應用,在不少方面都經歷了一個從無到有的過程,測試也是如此。剛開始時,代碼都寫很差,更別提測試了,那時測試爲0。經歷過一段時間後, 測試覆蓋率在90%+。這就是個人從0到90。剩下的10,還有很長的路,且待下回分解。javascript
最近在負責一個基於nodejs的應用,在不少方面都經歷了一個從無到有的過程,測試也是如此。剛開始時,代碼都寫很差,更別提測試了,那時測試爲0。經歷過一段時間後,尤爲是看到 npm 上優秀的庫的測試覆蓋率都在100%時,痛下決心開始學習 nodejs 的測試, 目前這個應用的測試覆蓋率在90%+。這就是個人從0到90。剩下的10,還有很長的路,且待下回分解。html
對於開發者,測試的重要性毋庸置疑, 所謂」無測試不上線「,」無測試不重構「等。但在實踐的過程當中,測試老是有這樣的那樣的問題,隨便列舉下:java
一千我的眼中有至少二千種測試理念或方法,很難定義什麼是一種好的測試。在團隊一直負責測試的推動,咱們的理念很簡單,包括如下幾個原則:node
一個好的測試,最重要的衡量標準就是測了多少(覆蓋率),75%是最低的標準。這個標準對java來講基本可行,但對nodejs不太適用,javascript是弱類型的、動態語言,沒有編譯階段,不少錯誤只能在運行時纔會被發現,因此須要更高的覆蓋率,最好是100%,目前我的的標準是90%+。git
每個測試用例,不管在任何環境下,都應該能夠反覆執行,併產生相同的結果。只有這樣,纔可以相任你的測試,進而發現真正的bug。這也是集成測試最低要求。github
一個測試用例只測試代碼的某一方面,如一個分支,且不強依賴某些特定的環境或條件。npm
不管是單個測試用例,仍是集成測試,必需要保證測試執行足夠的快。json
測試測什麼主要依據具體的需求、業務、成本、語言等,但也有必定的共性,單元測試準則 給出了一些準則可供參考,這裏再也不討論。小程序
又是一個很是大的話題,本文僅從我的角度給出nodejs測試的工具及方法。瀏覽器
Nodejs 測試框架衆多,目前使用最廣的是Mocha。本文詳細說明下 Mocha, 並簡要介紹幾種其它框架。本文的示例,如無特別說明,都是基於Mocha.
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 容許你使用任何你想用的斷言庫,包括:
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 提供的命令行外,還可使用 編輯器插件 運行,目前支持了:
以JetBrains爲例,JetBrain 爲其 IDE 套件(IntelliJ IDEA, WebStorm等)提供了 NodeJS, 能夠直接運行或調試 mocha 測試用例。基本步驟:
NodeJS
的插件並安裝;Edit Configuration
添加 Mocha
, 示例以下:如下列舉幾個基於 nodejs 的測試框架或工具,它們可用於 nodejs 或 瀏覽器端javascript 代碼的測試,不在本文討論範圍,詳見官方文檔。
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.
更多見 官方文檔.
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 提供對 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); }); });
更多見 文檔
代碼覆蓋(Code 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
是能夠在瀏覽器打開的覆蓋率報告,以下圖所示:
常見的測試模式包括TDD, BDD。
測試驅動開發是敏捷開發中的一項核心實踐和技術,也是一種設計方法論。TDD的原理是在開發功能代碼以前,先編寫單元測試用例代碼,測試代碼肯定須要編寫什麼產品代碼。TDD的基本思路就是經過測試來推進整個開發的進行,但測試驅動開發並不僅是單純的測試工做,而是把需求分析,設計,質量控制量化的過程。總體流程以下:
以一個階乘的小程序爲例。先寫出的測試用例,以下所示。此時運行確定會報錯,由於被測試的程序尚未寫。
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);
};
複製代碼
此時再運行以前的測試用例,看其是否經過,若是所有經過則表示開發完成,如不經過則反覆修改被測試的邏輯,直到所有的經過爲止。
行爲驅動開發是一種敏捷軟件開發的技術,它鼓勵軟件項目中的開發者、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和BDD本質和目標都是一致的。只是在實施方法上,進行了不一樣的探討來完善整個敏捷開發的體系。TDD的迭代反覆驗證是敏捷開發的保障,但沒有明確如何根據設計產生測試,並保障測試用例的質量,而BDD倡導你們都用簡潔的天然語言描述系統行爲的理念,剛好彌補了測試用例(即系統行爲)的準確性。
幾乎全部基於Nodejs的庫或應用都選擇了BDD, 至於爲何,我尚未搞明白。
目前比較流行的斷言庫包括:
這些斷言庫除了風格上略有不一樣外,實如今功能幾乎徹底一致,可根據我的喜愛或應用需求選擇。