學習測試框架Mocha
注意:是參考阮老師的文章來學的。雖然阮老師有講解,但是覺得自己敲一遍,然後記錄一遍效果會更好點。俗話說,好記性不如爛筆頭。
Mocha 是javascript測試框架之一,可以在瀏覽器和Node環境下使用,除了Mocha測試框架之外,類似的測試框架還有Jasmine, Karma, Tape等。
可以使用npm全域性安裝:如下命令:
npm install -g mocha
也可以作為專案的依賴進行安裝,如下命令:
npm install --save-dev mocha
如下所有的測試程式碼在github上,請檢視github上的程式碼
Mocha的作用是執行測試指令碼,我們先來編寫一個js程式碼吧,下面是一個簡單的加法模組 add.js程式碼:
function add(x, y) { return x + y; } module.exports = add;
要測試上面的程式碼是否對的,因此就要編寫測試指令碼,測試指令碼與所要測試的原始碼指令碼同名,但是字尾名為 .test.js或 .spec.js, 如:xx.test.js 或 xx.spec.js,比如上面的add.js的測試指令碼可以叫 add.test.js 或 add.spec.js,因此我們可以在add.js的同目錄下新建 add.test.js,(可以檢視demo1檔案程式碼)
編寫程式碼如下:
var add = require('./add.js'); var expect = require('chai').expect; describe('加法函式的測試', function() { it('1加1應該等於2', function() { expect(add(1, 1)).to.be.equal(2); }); });
如上程式碼就是一個測試指令碼程式碼,測試指令碼可以包含一個或多個describe塊,describe塊稱為 "測試套件",表示一組相關的測試,它是一個函式,有兩個引數,第一個引數是測試套件的名稱,第二個引數是一個實際執行的函式。
每個describe塊也可以包含一個或多個it塊,it塊稱為 "測試用例",表示一個單獨的測試,是測試的最小單位,它也是一個函式,第一個引數也是測試用例的名稱,第二個引數是一個實際執行的函式。
二. 理解斷言庫
斷言庫可以理解為比較函式,也就是斷言函式是否和預期一致,如果一致則表示測試通過,如果不一致表示測試失敗。mocha本身是不包括斷言庫的,所以必須引入第三方斷言庫的,目前比較受歡迎的斷言庫有 should.js, expect.js, chai.
should.js BDD風格
expect.js expect風格的斷言
chai expect(), assert() 和 should的斷言
Mocha預設使用的是BDD的風格。expect和should都是BDD的風格,二者使用相同的鏈式語言來組織斷言的,但不同在於他們初始化斷言的方式,expect使用
建構函式來建立斷言物件例項,而should通過為 Object.prototype新增方法來實現斷言(should不支援IE),expect直接指向 chai.expect,
should則是 chai.should();
上面的程式碼中 expect 是斷言的意思,該作用是判斷原始碼的實際執行結果與預期結果是否一致,如果不一致就丟擲一個錯誤,因此在執行上面程式碼之前,
我們需要在專案中安裝 chai, 如下命令:
npm install --save-dev chai
所有的測試用例(it塊)都應該含有一句或多句斷言,是編寫測試用例的關鍵,Mocha本身不包含斷言,斷言是由斷言庫來實現的,因此需要先引入斷言庫。
如下程式碼:
var expect = require('chai').expect;
上面程式碼是引用 chai 斷言庫,使用的是 expect斷言風格。
expect 官網API(http://chaijs.com/api/bdd/).
如下是一些常用的比較;
// equal 相等或不相等 expect(4 + 5).to.be.equal(9); expect(4 + 5).to.be.not.equal(10); expect('hello').to.equal('hello'); expect(42).to.equal(42); expect(1).to.not.equal(true); expect({ foo: 'bar' }).to.not.equal({ foo: 'bar' }); expect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' }); // above 斷言目標的值大於某個value,如果前面有length的鏈式標記,則可以用來判斷陣列長度或者字串長度 expect(10).to.be.above(5); expect('foo').to.have.length.above(2); expect([ 1, 2, 3 ]).to.have.length.above(2); 類似的還有least(value)表示大於等於;below(value)表示小於;most(value)表示小於等於 // 判斷目標是否為布林值true(隱式轉換) expect('everthing').to.be.ok; expect(1).to.be.ok; expect(false).to.not.be.ok; expect(undefined).to.not.be.ok; expect(null).to.not.be.ok; // true/false 斷言目標是否為true或false expect(true).to.be.true; expect(1).to.not.be.true; expect(false).to.be.false; expect(0).to.not.be.false; // null/undefined 斷言目標是否為null/undefined expect(null).to.be.null; expect(undefined).not.to.be.null; expect(undefined).to.be.undefined; expect(null).to.not.be.undefined; // NaN 斷言目標值不是數值 expect('foo').to.be.NaN; expect(4).not.to.be.NaN; // 判斷型別大法(可以實現上面的一些例子):a/an expect('test').to.be.a('string'); expect({ foo: 'bar' }).to.be.an('object'); expect(foo).to.be.an.instanceof(Foo); expect(null).to.be.a('null'); expect(undefined).to.be.an('undefined'); expect(new Error).to.be.an('error'); expect(new Promise).to.be.a('promise'); // 包含關係:用來斷言字串包含和陣列包含。如果用在鏈式呼叫中,可以用來測試物件是否包含某key 可以混著用。 expect([1,2,3]).to.include(2); expect('foobar').to.contain('foo'); expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo'); // 判斷空值 expect([]).to.be.empty; expect('').to.be.empty; expect({}).to.be.empty; // match expect('foobar').to.match(/^foo/); // exist 斷言目標既不是null也不是undefined var foo = 'hi' , bar = null, baz; expect(foo).to.exist; expect(bar).to.not.exist; expect(baz).to.not.exist; // within斷言目標值在某個區間範圍內,可以與length連用 expect(7).to.be.within(5,10); expect('foo').to.have.length.within(2,4); expect([ 1, 2, 3 ]).to.have.length.within(2,4); // instanceOf 斷言目標是某個構造器產生的事例 var Tea = function (name) { this.name = name; } , Chai = new Tea('chai'); expect(Chai).to.be.an.instanceof(Tea); expect([ 1, 2, 3 ]).to.be.instanceof(Array); // property(name, [value]) 斷言目標有以name為key的屬性,並且可以指定value斷言屬性值是嚴格相等的,此[value]引數為可選,如果使用deep鏈式呼叫,可以在name中指定物件或陣列的引用表示方法 // simple referencing var obj = { foo: 'bar' }; expect(obj).to.have.property('foo'); expect(obj).to.have.property('foo', 'bar');// 類似於expect(obj).to.contains.keys('foo') // deep referencing var deepObj = { green: { tea: 'matcha' }, teas: [ 'chai', 'matcha', { tea: 'konacha' } ] }; expect(deepObj).to.have.deep.property('green.tea', 'matcha'); expect(deepObj).to.have.deep.property('teas[1]', 'matcha'); expect(deepObj).to.have.deep.property('teas[2].tea', 'konacha'); // ownproperty 斷言目標擁有自己的屬性,非原型鏈繼承 expect('test').to.have.ownProperty('length'); // throw 斷言目標丟擲特定的異常 var err = new ReferenceError('This is a bad function.'); var fn = function () { throw err; } expect(fn).to.throw(ReferenceError); expect(fn).to.throw(Error); expect(fn).to.throw(/bad function/); expect(fn).to.not.throw('good function'); expect(fn).to.throw(ReferenceError, /bad function/); expect(fn).to.throw(err); expect(fn).to.not.throw(new RangeError('Out of range.')); // satisfy(method) 斷言目標通過一個真值測試 expect(1).to.satisfy(function(num) { return num > 0; })
三. mocha測試程式碼如何執行?
上面的add.test.js 編寫完成後,我們需要執行測試程式碼了,進入add.test.js程式碼的目錄後,執行如下命令可執行:
mocha add.test.js
如下結果:
$ mocha add.test.js
加法函式的測試
✓ 1加1應該等於2
1 passing (10ms)
如上所示,表示測試指令碼通過了測試,共用一個測試用例,耗時10毫秒。
mocha命令後面也可以指定多個檔案,如下命令:
mocha xx.test.js yy.test.js
3-1 把測試檔案放入test目錄下
mocha預設執行test子目錄裡面的測試指令碼,我們一般情況下,可以把測試指令碼放在test目錄下,然後進入對應的目錄,直接執行mocha命令即可:
請看demo2;
如下目錄頁面:
demo2 |---- src | |--- add.js | |--- multiple.js | |--- reduce.js |---- test | |--- dir | | |--- multiple.test.js | | | |--- add.test.js
src/add.js 程式碼如下:
function add(x, y) { return x + y; } module.exports = add;
src/multiple.js程式碼如下:
function multiply(x, y) { return x * y; } module.exports = multiply;
src/reduce.js 程式碼如下:
function add(x, y) { return x - y; } module.exports = add;
test/add.test.js程式碼如下:
var add = require('../src/add.js'); var expect = require('chai').expect; describe('加法函式的測試', function() { it('1 加 1 應該等於 2', function() { expect(add(1, 1)).to.be.equal(2); }); it('任何數加0應該等於自身', function() { expect(add(1, 0)).to.be.equal(1); }); });
test/dir/multiple.test.js程式碼如下:
var multiply = require('../../src/multiply'); var expect = require('chai').expect; describe('乘法函式的測試', function() { it('1 乘 1 應該等於 1', function() { expect(multiply(1, 1)).to.be.equal(1); }); })
當我在demo2專案目錄下,執行 mocha 命令後,執行如下:
$ mocha 加法函式的測試 ✓ 1 加 1 應該等於 2 ✓ 任何數加0應該等於自身 2 passing (10ms)
我們可以看到,test子目錄裡面的測試指令碼執行了,但是test目錄下還有dir這樣的目錄裡面的測試指令碼檔案並沒有執行,所以我們可以得出一個結論是,mocha
命令只會執行test第一層目錄下所有檔案,並不能執行巢狀目錄下的檔案。
為了執行所有巢狀目錄下的檔案,我們可以 mocha命令後面加一個引數 --recursive 引數,如下命令:
$ mocha --recursive 加法函式的測試 ✓ 1 加 1 應該等於 2 ✓ 任何數加0應該等於自身 乘法函式的測試 ✓ 1 乘 1 應該等於 1 3 passing (11ms)
四. 理解使用萬用字元
命令列中測試指令碼檔案,可能會有多個指令碼檔案需要被測試,這時候我們可以使用萬用字元,來做批量操作。
比如我們在 demo2下新建spec目錄,檔案目錄變成如下結構:
demo2 |---- src | |--- add.js | |--- multiple.js | |--- reduce.js |---- test | |--- dir | | |--- multiple.test.js | | | |--- add.test.js | |----- spec | |--- add.js | |--- reduce.js
demo2/spec/add.js 程式碼如下:
var add = require('../src/add.js'); var expect = require('chai').expect; describe('加法函式的測試', function() { it('1 加 1 應該等於 2', function() { expect(add(1, 1)).to.be.equal(2); }); it('任何數加0應該等於自身', function() { expect(add(1, 0)).to.be.equal(1); }); });
demo2/spec/reduce.js程式碼如下:
var reduce = require('../src/reduce.js'); var expect = require('chai').expect; describe('減法函式的測試', function() { it('2 減 1 應該等於 1', function() { expect(reduce(2, 1)).to.be.equal(1); }); });
我們可以執行如下命令,執行多個測試指令碼檔案:
mocha spec/{add,reduce}.js
命令效果如下:
$ mocha spec/{add,reduce}.js 加法函式的測試 ✓ 1 加 1 應該等於 2 ✓ 任何數加0應該等於自身 減法函式的測試 ✓ 2 減 1 應該等於 1 3 passing (11ms)
或者直接後面加*號,匹配所有的檔案,和js中的正則類似:如下命令:
$ mocha spec/*.js 加法函式的測試 ✓ 1 加 1 應該等於 2 ✓ 任何數加0應該等於自身 減法函式的測試 ✓ 2 減 1 應該等於 1 3 passing (10ms)
五. 命令列引數常用的有哪些?
5.1 --help
--help引數,用來檢視Mocha的所有命令列引數,如下命令所示:
mocha --help
5.2 --reporter
--reporter引數用來指定測試報告的格式,預設是spec格式。
$ mocha
# 等同於
$ mocha --reporter spec
我們可以使用 mocha --reporters 命令檢視所有內建的報告格式。如下命令:
$ mocha --reporters dot - dot matrix doc - html documentation spec - hierarchical spec list json - single json object progress - progress bar list - spec-style listing tap - test-anything-protocol landing - unicode landing strip xunit - xunit reporter min - minimal reporter (great with --watch) json-stream - newline delimited json events markdown - markdown documentation (github flavour) nyan - nyan cat!
我們可以使用 mochawesome(http://adamgruber.github.io/mochawesome/) 模組,可以生成漂亮的HTML格式的報告。
首先我們需要安裝 mochawesome模組,如下命令列:
npm install --save-dev mochawesome
$ ../node_modules/.bin/mocha --reporter mochawesome 加法函式的測試 ✓ 1 加 1 應該等於 2 ✓ 任何數加0應該等於自身 2 passing (10ms) [mochawesome] Report JSON saved to /Users/tugenhua/個人demo/vue1204/mocha/demo2/mochawesome-report/mochawesome.json [mochawesome] Report HTML saved to /Users/tugenhua/個人demo/vue1204/mocha/demo2/mochawesome-report/mochawesome.html
因此會在demo2專案目錄下生成 mochawesome-report 檔案目錄,我們可以檢視 mochawesome/mochawesome.html檔案開啟看一下即可:
5.3 --watch
--watch 引數用來監聽指定的測試指令碼,只要測試指令碼有變化,就會自動執行mocha。我們在demo2目錄下,執行 mocha --watch命令,
然後修改指令碼檔案,可以看到如下:
$ mocha --watch 加法函式的測試 ✓ 1 加 1 應該等於 2 ✓ 任何數加0應該等於自身 2 passing (9ms) 加法函式的測試 11 ✓ 1 加 1 應該等於 2 11 ✓ 任何數加0應該等於自身 2 passing (2ms)
我在add.js 加了一句 console.log(11),上面可以看到也同樣重新執行了 mocha命令。
5.4 --bail
--bail引數指定只要有一個測試用例沒有通過,就停止執行後面的測試用例。
mocha --bail
5.5 --grep
--grep引數用於搜尋測試用例的名稱(即it塊的第一個引數),然後只執行到匹配的測試用例。
如下程式碼命令:
$ mocha 加法函式的測試 11 ✓ 1 加 1 應該等於 2 11 ✓ 任何數加0應該等於自身 2 passing (10ms) ~/個人demo/vue1204/mocha/demo2 on Dev_20171115_wealth! $ mocha --grep "1 加 1" 加法函式的測試 11 ✓ 1 加 1 應該等於 2 1 passing (12ms)
5.6 --invert
--invert參數列示只執行不符合條件的測試指令碼,必須與 --grep引數配合使用。
如下執行結果:
~/個人demo/vue1204/mocha/demo2 on Dev_20171115_wealth! $ mocha 加法函式的測試 11 ✓ 1 加 1 應該等於 2 11 ✓ 任何數加0應該等於自身 2 passing (13ms) ~/個人demo/vue1204/mocha/demo2 on Dev_20171115_wealth! $ mocha --grep "1 加 1" --invert 加法函式的測試 11 ✓ 任何數加0應該等於自身 1 passing (10ms)
六. 配置檔案 mocha.opts
Mocha的測試指令碼檔案 允許放在test目錄下面,但是我們也可以在test目錄下新建一個mocha.opts檔案,把命令列寫在該裡面,還是看demo2目錄結構,
在test目錄下新建 mocha.opts檔案,如下目錄結構:
demo2 |---- src | |--- add.js | |--- multiple.js | |--- reduce.js |---- test | |--- dir | | |--- multiple.test.js | | | |--- add.test.js | |--- mocha.opts | |----- spec | |--- add.js | |--- reduce.js
mocha.opts檔案寫入如下命令:
--recursive
--reporter tap
然後執行mocha命令,就可以執行測試中的所有測試程式碼:
~/個人demo/vue1204/mocha/demo2 on Dev_20171115_wealth! $ mocha 1..3 11 ok 1 加法函式的測試 1 加 1 應該等於 2 11 ok 2 加法函式的測試 任何數加0應該等於自身 ok 3 乘法函式的測試 1 乘 1 應該等於 1 # tests 3 # pass 3 # fail 0
當然如果測試用例不是存放在test子目錄下,可以在mocha.opts寫入如下內容:
server-tests --recursive --reporter tap
上面程式碼指定允許 server-tests 目錄及其子目錄之中的測試指令碼。
七: ES6的測試;
如果測試指令碼是用ES6編寫的,那麼允許測試之前,需要先用babel轉碼,我們在test目錄下新建 es6.test.js檔案,先看目錄結構如下:
demo2 |---- src | |--- add.js | |--- multiple.js | |--- reduce.js |---- test | |--- dir | | |--- multiple.test.js | | | |--- add.test.js | |--- es6.test.js | |--- mocha.opts | |----- spec | |--- add.js | |--- reduce.js
es6.test.js 程式碼如下:
import add from '../src/add.js'; import chai from 'chai'; let expect = chai.expect; describe('加法函式的測試', function() { it('1 加 1 應該等於 2', function() { expect(add(1, 1)).to.be.equal(2); }); it('任何數加0應該等於自身', function() { expect(add(1, 0)).to.be.equal(1); }); });
如果我們直接在demo2命令列中允許 mocha命令就會報錯,如下報錯:
$ mocha /Users/tugenhua/個人demo/vue1204/mocha/demo2/test/es6.test.js:1 (function (exports, require, module, __filename, __dirname) { import add from '../src/add.js'; ^^^^^^ SyntaxError: Unexpected token import
因此我們需要ES6轉碼,需要安裝Babel。命令如下:
npm install babel-core babel-preset-es2015 --save-dev
然後我們需要在專案的目錄下,新建一個 .babelrc配置檔案。
新增如下程式碼如下:
{ "presets": ['es2015'] }
最後,我們使用 --compilers 引數指定測試指令碼的轉碼器。
../node_modules/mocha/bin/mocha --compilers js:babel-core/register
如上命令程式碼,--compilers 引數後面是一個使用冒號分割的字串,冒號左邊是檔案的字尾名,右邊是用來處理這一類檔案的模組名,意思是說,先使用
babel-core/register模組,處理一下 .js檔案;如下命令列程式碼所示:
~/個人demo/vue1204/mocha/demo2 on Dev_20171115_wealth! $ ../node_modules/mocha/bin/mocha --compilers js:babel-core/register 1..5 (node:55513) DeprecationWarning: "--compilers" will be removed in a future version of Mocha; see https://git.io/vdcSr for more info 11 ok 1 加法函式的測試 1 加 1 應該等於 2 11 ok 2 加法函式的測試 任何數加0應該等於自身 ok 3 乘法函式的測試 1 乘 1 應該等於 1 11 ok 4 加法函式的測試 1 加 1 應該等於 2 11 ok 5 加法函式的測試 任何數加0應該等於自身 # tests 5 # pass 5 # fail 0
注意點:Babel預設不會對 Iterator, Generator, Promise, Map, Set等全域性物件,以及一些全域性物件的方法(比如Object.assign)轉碼,
如果我們想要對這些物件轉碼,我們需要安裝 babel-polyfill.
npm install --save-dev babel-polyfill
最後,需要在我們的指令碼頭部加上 如下引入 babel-polyfill程式碼
import 'babel-polyfill'
八,非同步測試
先在mocha專案目錄下 新建檔案demo3,如下目錄結構:
demo3
|---- timeout.test.js
timeout.test.js程式碼如下:
var expect = require('chai').expect; describe('timeout.test.js - 超時測試', function() { it('測試應該 5000 毫秒後結束', function(done) { var x = true; var f = function() { x = false; expect(x).to.be.not.ok; done(); }; setTimeout(f, 4000); }); });
然後在demo3目錄下,執行命令列 mocha timeout.test.js, 執行如下:
~/個人demo/vue1204/mocha/demo3 on Dev_20171115_wealth! $ mocha timeout.test.js timeout.test.js - 超時測試 1) 測試應該 5000 毫秒後結束 0 passing (2s) 1 failing 1) timeout.test.js - 超時測試 測試應該 5000 毫秒後結束: Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
可以看到如上報錯 Timeout of 2000ms exceeded, 這是因為mocha預設每個測試用例最多執行2000毫秒,如果超過這個時間沒有返回結果,就會報錯,
所以我們在進行非同步操作的時候,需要額外指定timeout的時間的。因為非同步的操作是需要4000毫秒,所以我們指定5000毫秒就不會報錯了。
如下命令:
mocha --timeout 5000 timeout.test.js
如下執行結果:
~/個人demo/vue1204/mocha/demo3 on Dev_20171115_wealth! $ mocha --timeout 5000 timeout.test.js timeout.test.js - 超時測試 ✓ 測試應該 5000 毫秒後結束 (4008ms) 1 passing (4s)
這樣就保證測試用例成功了。
Mocha內建對Promise的支援,允許直接返回Promise. 在demo3目錄下 新建 promise.test.js, 如下目錄結構:
demo3 |---- timeout.test.js |---- promise.test.js
promise.test.js 程式碼如下:
var fetch = require('node-fetch'); var expect = require('chai').expect; describe('promise非同步測試', function() { it('非同步請求應該返回一個物件', function() { return fetch("https://api.github.com") .then(function(res) { return res.json() }).then(function(json) { expect(json).to.be.an("object"); }) }) });
然後執行命令如下:
~/個人demo/vue1204/mocha/demo3 on Dev_20171115_wealth!
$ mocha promise.test.js
promise非同步測試
✓ 非同步請求應該返回一個物件 (1165ms)
1 passing (1s)
如上可以看到也是可以成功的。
九:測試用例的鉤子
Mocha在describe塊之中,提供了測試用例的四個鉤子,before(), after(), beforeEach()和afterEach(),他們會在指定的時間內執行。
程式碼如下:
describe('hooks', function() { before(function(){ // 在本區塊的所有測試用例之前執行 }); after(function(){ // 在本區塊的所有測試用例之後執行 }); beforeEach(function(){ // 在本區塊的每個測試用例之前執行 }); afterEach(function(){ // 在本區塊的每個測試用例之後執行 }); });
before(): 將會在所有測試用例執行之前執行,比如在之前插入資料等等操作。
after(): 會在所有測試執行之後執行,用於清理測試環境,回滾到清空資料狀態。
beforeEach(): 將會在每個測試用例執行之前執行,可用於測試測試需要準備相關資料的條件。
afterEach(): 將會在每個測試用例之後執行,可用於準備測試用例所需的後置條件。
請看如下demo,在來理解下 mocha的四個鉤子函式,
在專案的根目錄下 新建demo4,目錄結構如下:
demo4 |---- src | |-- hooks.js |---- test | |--- hooks.test.js
hooks.js 程式碼如下:
// 儲存使用者物件 var saveUserObj = {}; // 定義使用者類 function User (name) {} // 儲存使用者 User.save = function(name) { saveUserObj[name] = name; } // 刪除使用者 User.delete = function(name) { delete saveUserObj[name]; } // 檢查是否包含該使用者 User.contains = function(name) { return saveUserObj[name] !== null; } // 返回所有的資料 User.getUsers = function() { return saveUserObj; } module.exports = User;
hooks.test.js程式碼如下:
var should = require('should'); var User = require('../src/hooks.js'); // 描述User的行為 describe('描述User的行為', function(){ // 執行所有測試之前,執行before函式,新增資料 before(function(){ User.save('kongzhi111'); console.log(User.getUsers()); // 列印出{kongzhi111: 'kongzhi111'} console.log(111111111111111111111); }); // 在執行每個測試前,執行beforeEach函式,新增資料 beforeEach(function() { User.save('kongzhi222'); console.log(User.getUsers()); // 列印出 {kongzhi111: 'kongzhi111', kongzhi222: 'kongzhi222'} console.log(222222222222222222222222) }) // 描述User.save的行為 describe('描述User.save的行為', function() { // 儲存kongzhi333成功了 it('儲存kongzhi333成功了', function() { User.save('kongzhi333'); console.log(User.getUsers()); // 列印出 {kongzhi111: 'kongzhi111', kongzhi222: 'kongzhi222', kongzhi333: 'kongzhi333'} console.log(33333333333333333333); }) }); // 描述User.contains的行為 describe('描述User.contains的行為', function(){ it('kongzhi111是存在的', function(){ User.contains('kongzhi111').should.be.exactly(true); }); it('kongzhi222是存在的', function(){ User.contains('kongzhi222').should.be.exactly(true); }); it('kongzhi333是存在的', function(){ User.contains('kongzhi333').should.be.exactly(true); }); it('kongzhi555是不存在', function(){ User.contains('kongzhi555').should.be.exactly(true); }); }); // 在執行完每個測試後,清空資料 afterEach(function() { User.delete('kongzhi222'); console.log(User.getUsers()); // 列印 {kongzhi111: 'kongzhi111', kongzhi333: 'kongzhi333'} console.log(44444444444444444444444); }); // 在執行完每個測試後,清空資料 after(function() { User.delete('kongzhi111'); console.log(User.getUsers()); // 列印 {kongzhi333: 'kongzhi333'} console.log(555555555555555555555555); User.delete('kongzhi333'); console.log(User.getUsers()); // 列印 {} }); })
在demo4下 執行mocha,執行命令後 如下:
~/個人demo/vue1204/mocha/demo4 on Dev_20171115_wealth! $ mocha 描述User的行為 { kongzhi111: 'kongzhi111' } 111111111111111110000 描述User.save的行為 { kongzhi111: 'kongzhi111', kongzhi222: 'kongzhi222' } 2.2222222222222222e+23 { kongzhi111: 'kongzhi111', kongzhi222: 'kongzhi222', kongzhi333: 'kongzhi333' } 33333333333333330000 ✓ 儲存kongzhi333成功了 { kongzhi111: 'kongzhi111', kongzhi333: 'kongzhi333' } 4.4444444444444445e+22 描述User.contains的行為 { kongzhi111: 'kongzhi111', kongzhi333: 'kongzhi333', kongzhi222: 'kongzhi222' } 2.2222222222222222e+23 ✓ kongzhi111是存在的 { kongzhi111: 'kongzhi111', kongzhi333: 'kongzhi333' } 4.4444444444444445e+22 { kongzhi111: 'kongzhi111', kongzhi333: 'kongzhi333', kongzhi222: 'kongzhi222' } 2.2222222222222222e+23 ✓ kongzhi222是存在的 { kongzhi111: 'kongzhi111', kongzhi333: 'kongzhi333' } 4.4444444444444445e+22 { kongzhi111: 'kongzhi111', kongzhi333: 'kongzhi333', kongzhi222: 'kongzhi222' } 2.2222222222222222e+23 ✓ kongzhi333是存在的 { kongzhi111: 'kongzhi111', kongzhi333: 'kongzhi333' } 4.4444444444444445e+22 { kongzhi111: 'kongzhi111', kongzhi333: 'kongzhi333', kongzhi222: 'kongzhi222' } 2.2222222222222222e+23 ✓ kongzhi555是不存在 { kongzhi111: 'kongzhi111', kongzhi333: 'kongzhi333' } 4.4444444444444445e+22 { kongzhi333: 'kongzhi333' } 5.5555555555555555e+23 {}
可以看到如上測試結果後的執行,試著理解一下,應該可以理解mocha中的4各鉤子函式的含義了。
理解非同步鉤子函式
在demo4目錄下的test檔案下 新建 hooks-async.test.js 用於測試非同步的程式碼
demo4 |---- src | |-- hooks.js |---- test | |--- hooks.test.js | |--- hooks-async.test.js
hooks-async.test.js 程式碼如下:
var expect = require('chai').expect; describe('非同步鉤子函式', function() { var foo = false; beforeEach(function(){ setTimeout(function(){ foo = true; }, 50) }); it('非同步鉤子函式成功', function() { expect(foo).to.be.equal(true); }) });
執行結果如下:
非同步鉤子函式 非同步鉤子函式成功: AssertionError: expected false to equal true + expected - actual -false +true
如上可以看到測試失敗,原因是因為setTimeout 是非同步的,在setTimeout執行完之前,it函式已經被執行了,所以foo當時資料還是false,
因此false不等於true了。
這時候 done引數出來了,在回撥函式存在時候,它會告訴mocha,你正在編寫一個非同步測試,會等到非同步測試完成的時候來呼叫done函式。
或者超過2秒後超時,如下程式碼就可以成功了;
hooks-async.test.js 程式碼如下:
var expect = require('chai').expect; describe('非同步鉤子函式', function() { var foo = false; beforeEach(function(done){ setTimeout(function(){ foo = true; // complete the async beforeEach done(); }, 50) }); it('非同步鉤子函式成功', function() { expect(foo).to.be.equal(true); }); });
10. 理解測試用例的管理
一個指令碼中可能有很多測試用例,有時候,我們想只執行其中的幾個,這時候我們使用only方法。describe塊和it塊都允許呼叫only方法,
表示只執行某個測試套件或測試用例。
在專案中新建檔案demo5,結構如下:
demo5 |---- src | |-- add.js |---- test | |--- add.test.js
add.test.js 程式碼如下:
var expect = require('chai').expect; var add = require('../src/add.js'); it.only('1 加 1應該等於2', function() { expect(add(1, 1)).to.be.equal(2); }); it('任何數加0應該等於自身', function() { expect(add(1, 0)).to.be.equal(1); });
進入demo5目錄,執行mocha命令後,如下:
~/個人demo/vue1204/mocha/demo5 on Dev_20171115_wealth! $ mocha ✓ 1 加 1應該等於2 1 passing (8ms)
可以看到只執行了 only方法。
十一:瀏覽器測試
除了在命令列執行,mocha還可以在瀏覽器下執行。
首先,使用 mocha init 命令在在指定的目錄生成初始化檔案。
mocha init demo6
在mocha目錄下 執行上面的命令後,會在demo6生成 index.html. mocha.css, mocha.js 和 tests.js 檔案。
index.html程式碼如下:
<!DOCTYPE html> <html> <head> <title>Mocha</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="mocha.css" /> </head> <body> <div id="mocha"></div> <script src="mocha.js"></script> <script>mocha.setup('bdd');</script> <script src="tests.js"></script> <script> mocha.run(); </script> </body> </html>
然後在demo6 下 新建一個 src/add.js 檔案
程式碼如下:
function add(x, y) { return x + y; }
然後,把這個檔案,以及斷言庫chai.js,加入index.html。
程式碼如下:
<!DOCTYPE html> <html> <head> <title>Mocha</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="mocha.css" /> </head> <body> <div id="mocha"></div> <!-- 瀏覽器新加的測試程式碼 --> <script type="text/javascript" src="src/add.js"></script> <script type="text/javascript" src="http://chaijs.com/chai.js"></script> <script src="mocha.js"></script> <script>mocha.setup('bdd');</script> <script src="tests.js"></script> <script> mocha.run(); </script> </body> </html>
然後在 tests.js 新增如下程式碼:
var expect = chai.expect; describe('加法函式的測試', function() { it('1 加 1 應該等於 2', function() { expect(add(1, 1)).to.be.equal(2); }); it('任何數加0等於自身', function() { expect(add(1, 0)).to.be.equal(1); expect(add(0, 0)).to.be.equal(0); }); });
執行index.html 即可看到效果。
十二: 生成規格檔案
Mocha支援從測試用例生成規格檔案。
在mocha-demo專案內 新建demo7檔案,該目錄檔案存放如下檔案
如下目錄頁面:
demo7 |---- src | |--- add.js | |--- multiple.js |---- test | |--- dir | | |--- multiple.test.js | | | |--- add.test.js
進入demo7目錄,執行如下命令:
$ mocha --recursive -R markdown >spec.md
就會在該目錄下 生成 spec.md 檔案,-R markdown引數指定規格報告是markdown格式。
如果想生成HTML格式的報告spec.html,使用下面的命令。
$ mocha --recursive -R doc > spec.html
就會在該目錄下 生成 spec.html檔案。