學習測試框架Mocha


學習測試框架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)

  加法函數的測試
111 加 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                

  加法函數的測試
111 加 1 應該等於 2
11
    ✓ 任何數加0應該等於自身


  2 passing (10ms)


~/個人demo/vue1204/mocha/demo2 on  Dev_20171115_wealth!
$ mocha --grep "1 加 1"

  加法函數的測試
111 加 1 應該等於 2


  1 passing (12ms)

5.6 --invert
--invert參數表示只運行不符合條件的測試腳本,必須與 --grep參數配合使用。
如下執行結果:

 

~/個人demo/vue1204/mocha/demo2 on  Dev_20171115_wealth! 
$ mocha                         

  加法函數的測試
111 加 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文件。

git上查看demo源碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM