就先從基本的單元測試開始,看看有哪些工具可以使用。
node.js可用的單元測試工具
node.js的單元測試工具其實不少,node.js核心模組也有提供assertion的支援。之前看過的工具重要的有:
在node.js中做單元測試,有一個重點是要能支援在非同步的操作中,也能進行單元測試,這幾個框架都能支援。不過expresso跟vow感覺比nodeunit複雜一點,所以我就選了nodeunit。因為我看過他的readme,就可以寫出單元測試了,學起來很快。
nodeunit可以透過一個命令列工具直接驅動測試,還可以選擇不同的報告格式。可以傳送單個測試的js檔案給它,或是整個目錄,它就會把目錄中的單元測試js檔都執行一次。
一個簡單的nodeunit單元測試長這樣:
exports.testSomething = function(test){
test.expect(1);
test.ok(true, "this assertion should pass");
test.done();
};
exports.testSomethingElse = function(test){
test.ok(false, "this assertion should fail");
test.done();
};
它會用參數傳送一個test物件給每個單元測試函數,使用test.expect(n)告訴它會有多少個assertion,測試結束則呼叫test.done()。需要做的動作就只有這些。
另外,它還支援test suite,利用這個還可以寫簡單的setup與teardown,來初始化一些測試環境。另外,還可以用group方式把幾個測試函數及合成群組。
var testCase = require('nodeunit').testCase;
module.exports = testCase({
setUp: function (callback) {
this.foo = 'bar';
callback();
},
tearDown: function (callback) {
// clean up
callback();
},
test1: function (test) {
test.equals(this.foo, 'bar');
test.done();
}
});
使用testCase,可以讓測試程式組織更清楚。
幾個單元測試的例子
首先,要把之前寫的testroute.js來改寫成單元測試...不過寫一些會發現,測試起來有點複雜,尤其在測試addhost時,所以稍微調整一下程式,增加一個checkhost函數,其他程式維持不變:
......
function checkhost(host) {
var a = cache.get(host, 'fs');
var b = cache.get(host, 'route');
if(typeof a == 'undefined' || typeof b == 'undefined') {
return false;
} else {
return true;
}
}
module.exports = {
addhost: addhost,
addfs: addfs,
addroute: addroute,
query: query,
checkhost: checkhost
};
這樣就可以用比較簡單的方式測試addhost:
var testCase = require('nodeunit').testCase;
module.exports = testCase({
setUp: function(cb) {
this.router = require('../lib/router');
cb();
},
tearDown: function(cb) {
this.router = null;
cb()
},
"testAddHost": function(test) {
test.expect(1);
var host = 'nodejs.org';
this.router.addhost(host);
test.ok(this.router.checkhost(host));
test.done();
}
});
然後先用nodeunit來跑跑看...咦?怎麼出錯了...可疑點在nodeunit/bin/nodeunit的十四行:
var args = process.ARGV.slice(2);
原來這週node-v0.6.0準備釋出,我使用的node-v0.5.10已經先把一些過時的API刪掉了...只要把ARGV改為小寫就可以了,跑出來的測試結果:
Feng-Hsu-Pingteki-MacBook-Air:evolve fillano$ nodeunit tests/testroute.js
testroute.js
✔ testAddHost
OK: 1 assertions (56ms)
Feng-Hsu-Pingteki-MacBook-Air:evolve fillano$
另外,在nodeunit/deps/ejs.js裡面使用了就的模組名稱,雖然不影響執行,但是每次跑都會出現一些提示訊息,也蠻討厭的,一起改掉吧。
require('sys') => require('util')
這樣就沒多大問題了。
與ant整合
改寫一下之前做的build.xml,讓它可以驅動nodeunit:
<?xml version="1.0"?>
<project name="evolve">
<target name="unittest">
<exec executable="D:/node/bin/nodeunit.cmd">
<arg value="tests/testroute.js" />
</exec>
</target>
<target name="integrate">
</target>
<target name="coverage-init">
</target>
<target name="coverage" depends="coverage-init">
</target>
<target name="lint">
</target>
<target name="alltest" depends="lint,coverage,unittest,integrate">
</target>
<target name="dummy">
</target>
</project>
然後跑ant unittest
來執行單元測試:
怎麼跑出奇怪的東西啦...看起來是因為做彩色輸出以及utf-8符號的關係,只好稍微調整他的外觀,拿他的eclipse.js這個reporter改一改,把utf-8符號以及顏色的escape code改掉,取名為ant.js,然後修改nodeunit/lib/reporters/index.js,把ant.js加進去,接下來只要在build.xml多加reporter的參數(怎麼使用參數,其實直接行nodeunit時,就會跑出說明)。這樣,就可以正常跑出單元測試結果了:
D:\fillano\Dropbox\Shared\evolve>ant unittest
Buildfile: D:\fillano\Dropbox\Shared\evolve\build.xml
unittest:
[exec]
[exec] testroute.js
[exec] ok: testAddHost
[exec]
[exec] OK: 1 assertions (8ms)
BUILD SUCCESSFUL
Total time: 0 seconds
可以正常輸出後,就可以撰寫更多的單元測試。