初識(shí)前端測(cè)試3 -- mocha

mocha

在第一小結(jié)中的測(cè)試中用到了 mocha 框架津函,這一節(jié)就說(shuō)說(shuō) mocha 框架吧屡久。下面整理的內(nèi)容主要來(lái)源于官網(wǎng)温眉,如需了解更多請(qǐng)移步mocha 官網(wǎng)惭等。

mocha 是一個(gè)功能豐富的前端測(cè)試框架苫费,mocha 既可以基于 Node.js 環(huán)境運(yùn)行也可以在瀏覽器環(huán)境運(yùn)行項(xiàng)目地址汤锨。

Mocha is a feature-rich JavaScript test framework running on Node.js and in the browser, making asynchronous testing simple and fun. Mocha tests run serially, allowing for flexible and accurate reporting, while mapping uncaught exceptions to the correct test cases. Hosted on GitHub.

安裝

安裝有兩種方式:1. 全局安裝;2. 將 mocha 作為項(xiàng)目依賴(lài)模塊安裝百框。

npm install --global mocha
npm install --save-dev mocha

起步

測(cè)試腳本的寫(xiě)法

Mocha 的作用是運(yùn)行測(cè)試腳本闲礼,首先必須學(xué)會(huì)寫(xiě)測(cè)試腳本。所謂"測(cè)試腳本",就是用來(lái)測(cè)試源碼的腳本柬泽。下面是一個(gè)加法模塊 add.js 的代碼慎菲。

// add.js
function add(x, y) {
  return x + y;
}

module.exports = add;

要測(cè)試這個(gè)加法模塊是否正確,就要寫(xiě)測(cè)試腳本聂抢。通常钧嘶,測(cè)試腳本與所要測(cè)試的源碼腳本同名,但是后綴名為.test.js(表示測(cè)試)或者.spec.js(表示規(guī)格)琳疏。比如有决,add.js 的測(cè)試腳本名字就是 add.test.js。

// add.test.js
var add = require('./add.js');
var expect = require('chai').expect;

describe('加法函數(shù)的測(cè)試', function() {
  it('1 加 1 應(yīng)該等于 2', function() {
    expect(add(1, 1)).to.be.equal(2);
  });
});

上面這段代碼空盼,就是測(cè)試腳本书幕,它可以獨(dú)立執(zhí)行。測(cè)試腳本里面應(yīng)該包括一個(gè)或多個(gè) describe 塊揽趾,每個(gè) describe 塊應(yīng)該包括一個(gè)或多個(gè) it 塊台汇。

describe 塊稱(chēng)為"測(cè)試套件"(test suite),表示一組相關(guān)的測(cè)試篱瞎。它是一個(gè)函數(shù)苟呐,第一個(gè)參數(shù)是測(cè)試套件的名稱(chēng)("加法函數(shù)的測(cè)試"),第二個(gè)參數(shù)是一個(gè)實(shí)際執(zhí)行的函數(shù)俐筋。

it 塊稱(chēng)為"測(cè)試用例"(test case)牵素,表示一個(gè)單獨(dú)的測(cè)試,是測(cè)試的最小單位澄者。它也是一個(gè)函數(shù)笆呆,第一個(gè)參數(shù)是測(cè)試用例的名稱(chēng)("1 加 1 應(yīng)該等于 2"),第二個(gè)參數(shù)是一個(gè)實(shí)際執(zhí)行的函數(shù)粱挡。

斷言庫(kù)

上面的測(cè)試腳本里面赠幕,有一句斷言。

expect(add(1, 1)).to.be.equal(2);

所謂"斷言"询筏,就是判斷源碼的實(shí)際執(zhí)行結(jié)果與預(yù)期結(jié)果是否一致榕堰,如果不一致就拋出一個(gè)錯(cuò)誤。上面這句斷言的意思是嫌套,調(diào)用 add(1, 1)逆屡,結(jié)果應(yīng)該等于 2。所有的測(cè)試用例(it 塊)都應(yīng)該含有一句或多句的斷言灌危。它是編寫(xiě)測(cè)試用例的關(guān)鍵康二。斷言功能由斷言庫(kù)來(lái)實(shí)現(xiàn),Mocha 本身不帶斷言庫(kù)勇蝙,所以必須先引入斷言庫(kù)沫勿。

var expect = require('chai').expect;

斷言庫(kù)有很多種挨约,Mocha 并不限制使用哪一種,它允許你使用你想要的任何斷言庫(kù)产雹。上面代碼引入的斷言庫(kù)是 chai诫惭,并且指定使用它的 expect 斷言風(fēng)格。下面這些常見(jiàn)的斷言庫(kù):

expect 斷言的優(yōu)點(diǎn)是很接近自然語(yǔ)言夕土,下面是一些例子。

// 相等或不相等
expect(4 + 5).to.be.equal(9);
expect(4 + 5).to.be.not.equal(10);
expect(foo).to.be.deep.equal({ bar: 'baz' });

// 布爾值為true
expect('everthing').to.be.ok;
expect(false).to.not.be.ok;

// typeof
expect('test').to.be.a('string');
expect({ foo: 'bar' }).to.be.an('object');
expect(foo).to.be.an.instanceof(Foo);

// include
expect([1, 2, 3]).to.include(2);
expect('foobar').to.contain('foo');
expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo');

// empty
expect([]).to.be.empty;
expect('').to.be.empty;
expect({}).to.be.empty;

// match
expect('foobar').to.match(/^foo/);

基本上瘟判,expect 斷言的寫(xiě)法都是一樣的怨绣。頭部是 expect 方法,尾部是斷言方法拷获,比如 equal篮撑、a/an、ok匆瓜、match 等赢笨。兩者之間使用 to 或 to.be 連接。如果 expect 斷言不成立驮吱,就會(huì)拋出一個(gè)錯(cuò)誤茧妒。事實(shí)上,只要不拋出錯(cuò)誤左冬,測(cè)試用例就算通過(guò)桐筏。

it('1 加 1 應(yīng)該等于 2', function() {});

上面的這個(gè)測(cè)試用例,內(nèi)部沒(méi)有任何代碼又碌,由于沒(méi)有拋出了錯(cuò)誤九昧,所以還是會(huì)通過(guò)绊袋。

在命令行中使用 mocha

在命令行使用 mocha 則需要在全局安裝:npm install mocha -g, 可以通過(guò)一些參數(shù)來(lái)測(cè)試指定的文件毕匀、指定展示結(jié)果的風(fēng)格、導(dǎo)出測(cè)試報(bào)告等癌别≡聿恚可使用mocha --help命令查看所有參數(shù)。

在項(xiàng)目中使用 mocha

初始化一個(gè) node 項(xiàng)目

mkdir step1 && cd step1
npm init -y
npm install --save-dev mocha

創(chuàng)建一個(gè)文件夾 test, 并在里面創(chuàng)建一個(gè) test.js ,寫(xiě)入下面的內(nèi)容展姐。

下面這段代碼主要是簡(jiǎn)單測(cè)試了一下數(shù)組 [1, 2, 3]indexOf() 方法躁垛。預(yù)期[1, 2, 3].indexOf(4)返回-1。

var assert = require('assert');
describe('Array', function() {
  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function() {
      assert.equal([1, 2, 3].indexOf(4), -1);
    });
  });
});

修改 package.json 文件圾笨。

{
  "scripts": {
    "test": "mocha"
  }
}

然后打開(kāi)終端教馆,在命令行中運(yùn)行npm run test, 下面是運(yùn)行結(jié)果。

image.png

mocha 基礎(chǔ)用法

測(cè)試回調(diào)方法被多次調(diào)用

如果使用基于回調(diào)的異步測(cè)試擂达,如果 done()被多次調(diào)用土铺,則 Mocha 將拋出錯(cuò)誤。這對(duì)于捕捉意外的雙重回調(diào)很方便。

it('double done', function(done) {
  // Calling `done()` twice is an error
  setImmediate(done);
  setImmediate(done);
});

回調(diào)多次意外運(yùn)行錯(cuò)誤結(jié)果

image.png

異步代碼檢查

使用 Mocha 測(cè)試異步代碼非常簡(jiǎn)單悲敷!只需在測(cè)試完成時(shí)調(diào)用回調(diào)究恤。通過(guò)向它添加一個(gè)回調(diào)函數(shù)(通常名為 done),Mocha 會(huì)知道它應(yīng)該等待這個(gè)函數(shù)被調(diào)用來(lái)完成測(cè)試后德。該回調(diào)接受 Error 實(shí)例(或其子類(lèi))或偽造值;其他任何事情都會(huì)導(dǎo)致測(cè)試失敗部宿。

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();
      });
    });
  });
});

為了使事情更簡(jiǎn)單,done()回調(diào)函數(shù)也接受一個(gè) Error 實(shí)例(即新的 Error())瓢湃,所以我們可以直接使用它:

describe('User', function() {
  describe('#save()', function() {
    it('should save without error', function(done) {
      var user = new User('Luna');
      user.save(done);
    });
  });
});

運(yùn)行 Promise

或者理张,您可以不使用 done()回調(diào),而是返回一個(gè) Promise绵患。如果您正在測(cè)試的 API 返回 Promise 而不是回調(diào)涯穷,這很有用:

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);
  });
});

使用 async/await

如果您的 JS 環(huán)境支持異步/等待,您也可以編寫(xiě)像這樣的異步測(cè)試:

beforeEach(async function() {
  await db.clear();
  await db.save([tobi, loki, jane]);
});

describe('#find()', function() {
  it('responds with matching records', async function() {
    const users = await db.find({ type: 'User' });
    users.should.have.length(3);
  });
});

測(cè)試同步的代碼

在測(cè)試同步代碼時(shí)藏雏,省略回調(diào)拷况,mocha 將自動(dòng)繼續(xù)下一次測(cè)試。

describe('Array', function() {
  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function() {
      [1, 2, 3].indexOf(5).should.equal(-1);
      [1, 2, 3].indexOf(0).should.equal(-1);
    });
  });
});

箭頭函數(shù)

不鼓勵(lì)在 mocha 中使用箭頭函數(shù)掘殴。 表達(dá)式中綁定的 this赚瘦,不能訪問(wèn) mocha 上下文。例如奏寨,以下代碼將失斊鹨狻:

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 的上下文,表達(dá)式是可以正常運(yùn)行的病瞳。但是揽咕,如果最終需要重構(gòu),結(jié)果可能會(huì)與預(yù)期有所不同套菜。

鉤子

憑借其默認(rèn)的“BDD”風(fēng)格界面亲善,Mocha 提供了 before(),after()逗柴,beforeEach()和 afterEach()之前的鉤子蛹头。這些應(yīng)該用于設(shè)置先決條件并在測(cè)試后進(jìn)行清理。

describe('hooks', function() {
  before(function() {
    // runs before all tests in this block
  });

  after(function() {
    // runs after all tests in this block
  });

  beforeEach(function() {
    // runs before each test in this block
  });

  afterEach(function() {
    // runs after each test in this block
  });

  // test cases
});

測(cè)試可以在你的鉤子之前戏溺,之后或穿插出現(xiàn)渣蜗。視情況而定,掛鉤將按其定義的順序運(yùn)行;所有 before() 鉤子運(yùn)行(一次)旷祸,然后任何 beforeEach() 鉤子耕拷,測(cè)試,任何 afterEach() 鉤子托享,以及最后的 after() 鉤子(一次)骚烧。

鉤子描述

任何鉤子都可以通過(guò)可選的描述來(lái)調(diào)用控淡,從而更容易查明測(cè)試中的錯(cuò)誤。如果鉤子被賦予了一個(gè)命名函數(shù)止潘,那么如果沒(méi)有提供描述掺炭,將使用該名稱(chēng)。

beforeEach(function() {
  // beforeEach hook
});

beforeEach(function namedFun() {
  // beforeEach:namedFun
});

beforeEach('some description', function() {
  // beforeEach:some description
});

異步鉤子

所有鉤子(before(), after(), beforeEach(), afterEach())都可能是同步或異步的凭戴,其行為與常規(guī)測(cè)試案例非常相似涧狮。例如下面的代碼,您可能希望在每次測(cè)試之前用虛擬內(nèi)容填充數(shù)據(jù)庫(kù):

describe('Connection', function() {
  var db = new Connection(),
    tobi = new User('tobi'),
    loki = new User('loki'),
    jane = new User('jane');

  // 每次測(cè)試前填充數(shù)據(jù)
  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();
      });
    });
  });
});

root 級(jí)別的鉤子

你也可以選擇任何文件并添加“root”級(jí)別的鉤子么夫。例如者冤,在所有 describe()塊之外添加 beforeEach()。這里定義的回調(diào) beforeEach()在任何測(cè)試用例之前運(yùn)行档痪,而不管它存在于哪個(gè)文件中(這是因?yàn)?Mocha 有一個(gè)隱含的 describe()塊涉枫,稱(chēng)為“root 套件”)。

beforeEach(function() {
  console.log('before every test in every file');
});

延遲執(zhí)行 root 套件

如果您需要在運(yùn)行任何套件之前執(zhí)行異步操作腐螟,則可能會(huì)延遲 root 套件愿汰。用--delay 標(biāo)志運(yùn)行 mocha。這將在全局上下文中附加一個(gè)特殊的回調(diào)函數(shù) run():

setTimeout(function() {
  // do some setup

  describe('my suite', function() {
    // ...
  });

  run();
}, 5000);

待定的測(cè)試用例

待定的測(cè)試用例就是指的那些最終需要完成而待完成測(cè)試的用例乐纸,這些實(shí)例只有描述而沒(méi)有會(huì)回調(diào)衬廷。待測(cè)試將包含在測(cè)試結(jié)果中,并標(biāo)記為待處理汽绢。未決測(cè)試不被視為失敗測(cè)試吗跋。如下面這種:

describe('Array', function() {
  describe('#indexOf()', function() {
    // pending test below
    it('should return -1 when the value is not present');
  });
});

運(yùn)行結(jié)果

image.png

測(cè)試用例管理

當(dāng)有很多測(cè)試用例。有時(shí)宁昭,我們希望只運(yùn)行其中的幾個(gè)跌宛,這時(shí)可以用 only 方法。describe 塊和 it 塊都允許調(diào)用 only 方法积仗,表示只運(yùn)行某個(gè)測(cè)試套件或測(cè)試用例疆拘。

describe('Array', function() {
  describe.only('#indexOf()', function() {
    // ...
  });
});

describe('Array', function() {
  describe('#indexOf()', function() {
    it.only('should return -1 unless present', function() {
      // ...
    });

    it('should return the index when present', function() {
      // ...
    });
  });
});

有時(shí)需要跳過(guò)一些測(cè)試用例可以使 skip 方法:

describe('Array', function() {
  describe.skip('#indexOf()', function() {
    // ...
  });
});

describe('Array', function() {
  describe('#indexOf()', function() {
    it.skip('should return -1 unless present', function() {
      // this test will not be run
    });

    it('should return the index when present', function() {
      // this test will be run
    });
  });
});

有時(shí)需要根據(jù)環(huán)境來(lái)判斷是否跳過(guò)或者指定運(yùn)行一些實(shí)例,可以參考下面的代碼

it('should only test in the correct environment', function() {
  if (/* check test environment */) {
    // make assertions
  } else {
    this.skip();
  }
});

it('should only test in the correct environment', function() {
  if (/* check test environment */) {
    // make assertions
  } else {
    // do nothing
  }
});

before(function() {
  if (/* check test environment */) {
    // setup code
  } else {
    this.skip();
  }
});
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末斥扛,一起剝皮案震驚了整個(gè)濱河市入问,隨后出現(xiàn)的幾起案子丹锹,更是在濱河造成了極大的恐慌稀颁,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件楣黍,死亡現(xiàn)場(chǎng)離奇詭異匾灶,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)租漂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)阶女,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)颊糜,“玉大人,你說(shuō)我怎么就攤上這事秃踩〕挠悖” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵憔杨,是天一觀的道長(zhǎng)鸟赫。 經(jīng)常有香客問(wèn)我,道長(zhǎng)消别,這世上最難降的妖魔是什么抛蚤? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮寻狂,結(jié)果婚禮上岁经,老公的妹妹穿的比我還像新娘。我一直安慰自己蛇券,他們只是感情好缀壤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著纠亚,像睡著了一般诉位。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上菜枷,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天苍糠,我揣著相機(jī)與錄音,去河邊找鬼啤誊。 笑死岳瞭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蚊锹。 我是一名探鬼主播瞳筏,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼牡昆!你這毒婦竟也來(lái)了姚炕?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤丢烘,失蹤者是張志新(化名)和其女友劉穎柱宦,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體播瞳,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掸刊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赢乓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忧侧。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡石窑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蚓炬,到底是詐尸還是另有隱情松逊,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布肯夏,位于F島的核電站棺棵,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏熄捍。R本人自食惡果不足惜烛恤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望余耽。 院中可真熱鬧缚柏,春花似錦、人聲如沸碟贾。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)袱耽。三九已至杀餐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間朱巨,已是汗流浹背史翘。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留冀续,地道東北人琼讽。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像洪唐,于是被迫代替她去往敵國(guó)和親钻蹬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

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