Mocha是什么
Mocha是一個Javascript測試框架滴某。只要是Javascript能運行的地方涝开,都可以運行mocha。其主要特征是能夠使得異步測試更加簡單以及提供了精確的報告。
安裝方法
使用npm
全局安裝 npm install -g mocha
碰镜;
作為開發(fā)依賴本地安裝 npm install --save-dev mocha
一個簡單的例子
var assert = require('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(4)); // pass
assert.equal(0, [1,2,3].indexOf(2)); // fail. index should be 1
});
});
});
上面的例子使用了測試集定義函數(shù)describe()
和測試用例定義函數(shù)it()
逃呼,還使用了node的assert模塊來做驗證來檢查數(shù)組索引的值是否匹配鳖孤。很明顯第一個驗證可以通過,而第二個不行抡笼∷沾В可以使用命令mocha
來執(zhí)行測試,也可以在package.json
里面配置了test
命令使用npm test
執(zhí)行推姻,無論哪種方式平匈,運行效果都如下
? mocha_demo mocha test.js
Array
#indexOf()
1) should return -1 when the value is not present
0 passing (9ms)
1 failing
1) Array
#indexOf()
should return -1 when the value is not present:
AssertionError [ERR_ASSERTION]: 0 == 1
+ expected - actual
-0
+1
at Context.<anonymous> (test.js:8:14)
把第二個測試用例期待值修改正確后,執(zhí)行通過的效果如下
? mocha_demo mocha test.js
Array
#indexOf()
? should return -1 when the value is not present
1 passing (7ms)
? mocha_demo
Mocha
的執(zhí)行會找到當前命令執(zhí)行目錄下的test
目錄藏古。./test/*.js
是Mocha
尋找的目標增炭。
Mocha
提供了多種便利的執(zhí)行方式
- 多個文件
mocha file1 file2 file3
- 指定目錄
mocha ./test/dir1/*.js
- 通配符
mocha ./spec/test*.js
,mocha ./spec/{my, awesome}.js
- node通配符
mocha './spec/**/*.@(js|jsx)'
驗證 assertions
mocha不像jasmine那樣自身內(nèi)建具有驗證功能的方法,但是我們可以在測試用例中使用諸如 should.js拧晕、expect.js 和chai等驗證庫來完成驗證功能隙姿。推薦使用chai。
異步代碼測試
當測試異步的代碼時厂捞,通過給it()
加一個回調(diào)函數(shù)(通常命名為done())即可讓mocha
知道異步代碼執(zhí)行之后需要調(diào)用done()
來表示測試完成
describe('User', function() {
describe('#save()', function() {
it('should save without error', function(done) {
var user = new User('Luna');
user.save(function(err) {
if (err) done(err);
else done();
});
});
});
});
當done()
可以接受異步代碼錯誤的時候孟辑,上面代碼還可以簡化為
describe('User', function() {
describe('#save()', function() {
it('should save without error', function(done) {
var user = new User('Luna');
user.save(done);
});
});
});
promises代碼測試
當異步代碼返回的是promise
而不是callback
的時候,用例也可以返回promise
beforeEach(function() {
return db.clear()
.then(function() {
return db.save([tobi, loki, jane]);
});
});
describe('#find()', function() {
it('respond with matching records', function() {
return db.find({ type: 'User' }).should.eventually.have.length(3); // https://www.npmjs.com/package/chai-as-promised
});
});
同步代碼測試
當測試同步的代碼時蔫敲,mocha
會自動的執(zhí)行下一個測試用例饲嗽,所以mocha
會連續(xù)地執(zhí)行所有的測試
'use strict';
const assert = require('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));
});
it('should not return -1 when the value is present', function() {
assert.equal(0, [1,2,3].indexOf(1));
assert.equal(1, [1,2,3].indexOf(2));
assert.equal(2, [1,2,3].indexOf(3));
});
});
describe('#length()', function() {
it('should have length 3', function() {
assert.equal(3, [1,2,3].length);
});
});
});
上面的測試代碼執(zhí)行效果如下
? mocha_demo mocha test.js
Array
#indexOf()
? should return -1 when the value is not present
? should not return -1 when the value is present
#length()
? should have length 3
3 passing (7ms)
箭頭函數(shù)
不建議在mocha
中使用箭頭函數(shù),因為箭頭函數(shù)對this
的綁定會使測試用例無法訪問Mocha
上下文中的一些方法奈嘿。
比如下面代碼this.timeout(1000)
就會執(zhí)行失敗
'use strict';
const assert = require('assert');
describe('my suite', () => {
it('my test', () => {
// should set the timeout of this test to 1000 ms; instead will fail
this.timeout(1000);
assert.ok(true);
});
});
? mocha_demo mocha test.js
my suite
1) my test
0 passing (7ms)
1 failing
1) my suite
my test:
TypeError: this.timeout is not a function
at Context.it (test.js:8:10)
把箭頭函數(shù)刪除后貌虾,就可以運行成功
'use strict';
const assert = require('assert');
describe('my suite', function() {
it('my test', function() {
// should set the timeout of this test to 1000 ms; instead will fail
this.timeout(1000);
assert.ok(true);
});
});
? mocha_demo mocha test.js
my suite
? my test
1 passing (6ms)
Hooks
Mocha
提供了四種hooks用來做測試準備和清理工作
- before() - 在所有測試套件運行之前執(zhí)行
- after() - 在所有測試套件運行之后執(zhí)行
- beforeEach() - 在每個測試用例運行之前執(zhí)行
- afterEach() - 在每個測試用例運行之后執(zhí)行
下面是一個簡單的例子
'use strict';
const assert = require('assert');
describe('my suite', function() {
before('This is before method description', function(){
console.log('this is before(), runs before all tests in this block');
});
after(function aftername(){
console.log('this is after(), runs after all tests in this block');
});
beforeEach(function(){
console.log('this is beforeEach(), runs before each test in this block');
});
afterEach(function(){
console.log('this is afterEach(), runs after each test in this block');
});
it('my test case 1', function() {
assert.ok(true);
});
it('my test case 2', function(){
assert.equal(1, 1);
})
});
describe('my suite 2', function(){
it('my test case 3', function(){
assert.equal('bar', 'bar')
})
});
? mocha_demo mocha test.js
my suite
this is before(), runs before all tests in this block
this is beforeEach(), runs before each test in this block
? my test case 1
this is afterEach(), runs after each test in this block
this is beforeEach(), runs before each test in this block
? my test case 2
this is afterEach(), runs after each test in this block
this is after(), runs after all tests in this block
my suite 2
? my test case 3
3 passing (11ms)
hooks也能用于異步代碼的處理
describe('Connection', function() {
var db = new Connection,
tobi = new User('tobi'),
loki = new User('loki'),
jane = new User('jane');
beforeEach(function(done) {
db.clear(function(err) {
if (err) return done(err);
db.save([tobi, loki, jane], done);
});
});
describe('#find()', function() {
it('respond with matching records', function(done) {
db.find({type: 'User'}, function(err, res) {
if (err) return done(err);
res.should.have.length(3);
done();
});
});
});
});
全局hooks
剛才提到的hooks
都是定義在describe()
里面,如果hooks
定義在了describe()
外面裙犹,那么這個hooks
的作用域就是整個文件的全部測試套件尽狠。這是因為mocha
有一個implied describe(),稱之為"root suite"叶圃。
'use strict';
const assert = require('assert');
before('This is before method description', function(){
console.log('this is before(), runs before all tests in this block');
});
after(function aftername(){
console.log('this is after(), runs after all tests in this block');
});
beforeEach(function(){
console.log('this is beforeEach(), runs before each test in this block');
});
afterEach(function(){
console.log('this is afterEach(), runs after each test in this block');
});
describe('my suite', function() {
it('my test case 1', function() {
assert.ok(true);
});
it('my test case 2', function(){
assert.equal(1, 1);
})
});
describe('my suite 2', function(){
it('my test case 3', function(){
assert.equal('bar', 'bar')
})
});
? mocha_demo mocha test.js
this is before(), runs before all tests in this block
my suite
this is beforeEach(), runs before each test in this block
? my test case 1
this is afterEach(), runs after each test in this block
this is beforeEach(), runs before each test in this block
? my test case 2
this is afterEach(), runs after each test in this block
my suite 2
this is beforeEach(), runs before each test in this block
? my test case 3
this is afterEach(), runs after each test in this block
this is after(), runs after all tests in this block
3 passing (13ms)
延遲測試
如果想要通過延遲測試執(zhí)行袄膏,而在測試之前完成某些特定的工作,我們需要命令mocha --delay
來實現(xiàn)掺冠。這個命令會把run()
函數(shù)加上全局環(huán)境中沉馆,等特定工作完成后調(diào)用run()
即可啟動測試執(zhí)行。
setTimeout(function() {
// do some setup
describe('my suite', function() {
// ...
});
run();
}, 5000);
待定測試
當一個測試沒有完成,需要以后來繼續(xù)完成的時候斥黑,這個測試就進入了pending待定狀態(tài)揖盘。為了測試執(zhí)行不被未完成的用例影響,我們只需要簡單地不添加回調(diào)函數(shù)callback即可锌奴。
'use strict';
const assert = require('assert');
describe('my suite 2', function(){
it('my test case 3')
});
上面的it()
沒有加上回調(diào)函數(shù)兽狭,于是執(zhí)行結(jié)果中 pending
就會+1
my suite 2
- my test case 3
0 passing (6ms)
1 pending
單獨執(zhí)行某一個測試套件或是測試用例
通過給describe()
和it()
加上.only()
,我們可以指定執(zhí)行只加上了.only()
的測試套件或是測試用例
下面這個列子是只執(zhí)行內(nèi)嵌的測試套件
'use strict';
const assert = require('assert');
describe('my suite 1', function(){
describe('my nested suite 1', function(){
it('my test case 1', function(){
console.log('this is test case 1');
});
it('my test case 2', function(){
console.log('this is test case 2');
});
});
describe.only('my nested suite 2', function(){
it('my test case 3', function(){
console.log('this is test case 3');
});
});
});
describe('my suite 2', function(){
it('my test case 4', function(){
console.log('this is test case 4');
});
it('my test case 5', function(){
console.log('this is test case 5');
});
});
? mocha_demo mocha test.js
my suite 1
my nested suite 2
this is test case 3
? my test case 3
1 passing (6ms)
下面列子指定了只執(zhí)行兩個測試用例鹿蜀,所以.only()
是可以使用多次來定義測試套件或是測試用例的子集
'use strict';
const assert = require('assert');
describe('my suite 1', function(){
describe('my nested suite 1', function(){
it('my test case 1', function(){
console.log('this is test case 1');
});
it.only('my test case 2', function(){
console.log('this is test case 2');
});
});
describe('my nested suite 2', function(){
it('my test case 3', function(){
console.log('this is test case 3');
});
});
});
describe('my suite 2', function(){
it('my test case 4', function(){
console.log('this is test case 4');
});
it.only('my test case 5', function(){
console.log('this is test case 5');
});
});
? mocha_demo mocha test.js
my suite 1
my nested suite 1
this is test case 2
? my test case 2
my suite 2
this is test case 5
? my test case 5
2 passing (8ms)
當測試套件和測試用例同時都加上了.only()
的時候箕慧,測試用例的執(zhí)行是優(yōu)先的。下面這個列子只會執(zhí)行it.only()
'use strict';
const assert = require('assert');
describe('my suite 1', function(){
describe.only('my nested suite 1', function(){
it.only('my test case 1', function(){
console.log('this is test case 1');
});
it('my test case 2', function(){
console.log('this is test case 2');
});
});
describe('my nested suite 2', function(){
it('my test case 3', function(){
console.log('this is test case 3');
});
});
});
? mocha_demo mocha test.js
my suite 1
my nested suite 1
this is test case 1
? my test case 1
1 passing (11ms)
如果有Hooks的話茴恰,Hooks會一直被執(zhí)行的
跳過測試
既然有指定執(zhí)行某幾個測試颠焦,當然Mocha
也提供不執(zhí)行某些測試,也就是跳過測試琐簇。通過給describe()
和it()
加上.skip()
蒸健,我們可以跳過只加上了.skip()
的測試套件或是測試用例座享。任何被跳過沒有執(zhí)行的測試用例都會被在結(jié)果中標記為pending
婉商。
下面的例子中,兩個測試套件都被跳過了渣叛,所有結(jié)果里面被標記為pending
'use strict';
const assert = require('assert');
describe('my suite 1', function(){
describe.skip('my nested suite 1', function(){
it('my test case 1', function(){
console.log('this is test case 1');
});
it('my test case 2', function(){
console.log('this is test case 2');
});
});
describe('my nested suite 2', function(){
it('my test case 3', function(){
console.log('this is test case 3');
});
});
});
describe.skip('my suite 2', function(){
it('my test case 4', function(){
console.log('this is test case 4');
});
it('my test case 5', function(){
console.log('this is test case 5');
});
});
? mocha_demo mocha test.js
my suite 1
my nested suite 1
- my test case 1
- my test case 2
my nested suite 2
this is test case 3
? my test case 3
my suite 2
- my test case 4
- my test case 5
1 passing (8ms)
4 pending
下面這個例子中丈秩,有兩個測試用例被跳過了
'use strict';
const assert = require('assert');
describe('my suite 1', function(){
describe('my nested suite 1', function(){
it('my test case 1', function(){
console.log('this is test case 1');
});
it.skip('my test case 2', function(){
console.log('this is test case 2');
});
});
describe('my nested suite 2', function(){
it('my test case 3', function(){
console.log('this is test case 3');
});
});
});
describe('my suite 2', function(){
it('my test case 4', function(){
console.log('this is test case 4');
});
it.skip('my test case 5', function(){
console.log('this is test case 5');
});
});
? mocha_demo mocha test.js
my suite 1
my nested suite 1
this is test case 1
? my test case 1
- my test case 2
my nested suite 2
this is test case 3
? my test case 3
my suite 2
this is test case 4
? my test case 4
- my test case 5
3 passing (8ms)
2 pending
使用.skip()
是比注釋更好的能夠不執(zhí)行指定測試的方法。
使用this.skip()
可以在運行時動態(tài)的跳過測試淳衙。比如一個測試需要在運行前檢查運行環(huán)境是否準備好了蘑秽,如果沒有準備好,那我們可以直接使用this.skip()
來跳過箫攀。
下面的例子表明正是由于環(huán)境有問題肠牲,需要跳過測試,最后跳過的測試會被標記為pending
靴跛。
'use strict';
const assert = require('assert');
let checkEnv = false;
describe('my suite 2', function(){
it('my test case 4', function(){
console.log('this is test case 4');
if (checkEnv === true){
console.log('Env is ready, lets do sth');
} else {
this.skip();
};
});
it('my test case 5', function(){
console.log('this is test case 5');
});
});
? mocha_demo mocha test.js
my suite 2
this is test case 4
- my test case 4
this is test case 5
? my test case 5
1 passing (8ms)
1 pending
this.skip()
后面一般不會有其他代碼缀雳。
一次性跳過多個測試的話,我們可以在'before' hook里使用this.skip()
下面例子中的beforeEach()
就使用了this.skip()
來跳過測試
'use strict';
const assert = require('assert');
let checkEnv = false;
describe('my suite 2', function(){
beforeEach(function(){
if (checkEnv === true){
console.log('Env is ready, lets do sth');
} else {
this.skip();
};
});
it('my test case 4', function(){
console.log('this is test case 4');
});
it('my test case 5', function(){
console.log('this is test case 5');
});
});
? mocha_demo mocha test.js
my suite 2
- my test case 4
- my test case 5
0 passing (6ms)
2 pending
重跑測試
當一個測試失敗的時候梢睛,我們可以使用this.retries(number)
來重新執(zhí)行失敗的測試肥印。這個功能是為了處理E2E功能測試失敗而設(shè)計的,所以不推薦在單元測試里面使用绝葡。
下面例子表明在describe()
里面使用this.retries(number)
會作用于全部的測試用例深碱,對于Hooks,只會作用于beforeEach()
和afterEach()
describe('retries', function() {
// Retry all tests in this suite up to 4 times
this.retries(4);
beforeEach(function () {
browser.get('http://www.yahoo.com');
});
it('should succeed on the 3rd try', function () {
// Specify this test to only retry up to 2 times
this.retries(2);
expect($('.foo').isDisplayed()).to.eventually.be.true;
});
});
動態(tài)生成測試
Mocha
使用Function.prototype.call
和函數(shù)表達式來定義測試套件和測試用例藏畅,這樣能夠很方便的動態(tài)生成測試用例敷硅。不需要特別的語法,只需要js就可以實現(xiàn)類似參數(shù)化測試的功能。
var assert = require('chai').assert;
function add() {
return Array.prototype.slice.call(arguments).reduce(function(prev, curr) {
return prev + curr;
}, 0);
}
describe('add()', function() {
var tests = [
{args: [1, 2], expected: 3},
{args: [1, 2, 3], expected: 6},
{args: [1, 2, 3, 4], expected: 10}
];
tests.forEach(function(test) {
it('correctly adds ' + test.args.length + ' args', function() {
var res = add.apply(null, test.args);
assert.equal(res, test.expected);
});
});
});
上面代碼會產(chǎn)生三個測試用例
? mocha_demo mocha test.js
add()
? correctly adds 2 args
? correctly adds 3 args
? correctly adds 4 args
3 passing (7ms)