mocha學習筆記1

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/*.jsMocha尋找的目標增炭。

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.jschai等驗證庫來完成驗證功能隙姿。推薦使用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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末竞膳,一起剝皮案震驚了整個濱河市航瞭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌坦辟,老刑警劉巖刊侯,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異锉走,居然都是意外死亡滨彻,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門挪蹭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來亭饵,“玉大人,你說我怎么就攤上這事梁厉」佳颍” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵词顾,是天一觀的道長八秃。 經(jīng)常有香客問我,道長肉盹,這世上最難降的妖魔是什么昔驱? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮上忍,結(jié)果婚禮上骤肛,老公的妹妹穿的比我還像新娘。我一直安慰自己窍蓝,他們只是感情好腋颠,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吓笙,像睡著了一般淑玫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上观蓄,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天混移,我揣著相機與錄音,去河邊找鬼侮穿。 笑死歌径,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的亲茅。 我是一名探鬼主播回铛,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼狗准,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了茵肃?” 一聲冷哼從身側(cè)響起腔长,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎验残,沒想到半個月后捞附,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡您没,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年鸟召,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氨鹏。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡欧募,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出仆抵,到底是詐尸還是另有隱情跟继,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布镣丑,位于F島的核電站舔糖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏传轰。R本人自食惡果不足惜剩盒,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一谷婆、第九天 我趴在偏房一處隱蔽的房頂上張望慨蛙。 院中可真熱鬧,春花似錦纪挎、人聲如沸期贫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽通砍。三九已至,卻和暖如春烤蜕,著一層夾襖步出監(jiān)牢的瞬間封孙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工讽营, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留虎忌,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓橱鹏,卻偏偏與公主長得像膜蠢,于是被迫代替她去往敵國和親堪藐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容