Mocha單元測試

寫這篇文章的初衷是,有個朋友來找我寫單元測試...本身懵逼了,開發了這麼久,還真沒有好好寫過測試用例,而後就和我朋友掰扯,個人論點是,前端開發不必寫單元測試,通常公司都有端對端測試就是點點點...開發人員一旦書寫測試用例,無形增長人員的開銷,或者是延緩迭代速率...他一句話,上頭須要...兩人啞口無言

乘着空閒的時候就查閱了各類資料
思考一:是否有必要單元測試?(團隊是否擁有測試團隊的兩種狀況)
思考二:實在要編寫單元測試如何編寫優雅的測試用例模塊?javascript

你們有空不妨告訴我大家公司會用到單元測試麼?有必要麼?前端

下面先自我歸總下Mocha單元測試的基礎api使用和注意點java

什麼是Mocha

字如其名,mocha (摩卡咖啡)官網文檔也是如此通常
JavaScript 具備多種工具和框架,這些工具和框架能夠在 Node.js 以及瀏覽器中進行測試驅動開發,例如:Jest, Jasmine, Mocha, QUnit, Karma, Cypress 等。
mocha就是測試驅動開發中衆多工具之一

第一個測試套件

安裝 所需依賴git

npm i mocha -g  全局安裝
npm i mocha --save-dev 項目依賴中安裝

終端中執行mocha將在當前目錄中須要test/文件夾並執行對應的測試用用例文件
下面咱們創建個test目錄並新建index.test.js文件github

// index.test.js

function add(v,v2){
    return v + v2;
}
 describe('# add()', function() {
    it('should return 3 when the value 1 、 2', function() {
      expect(add()).to.be.equal(3)
    })
  })

以上一個測試add()方法運算邏輯是否正常的功能測試用例就算是編寫好了
可是真實項目中,咱們通常不會如此簡單的業務邏輯;通常都會包含測試異步代碼、測試同步代碼,以及一些騷操做提升效率npm

異步代碼測試

異步代碼在現代開發中很常見,mocha針對異步代碼也有對應的解決方案
1.使用回調方法
2.使用 Promise (用於支持 Promise 的環境)
3.使用 async/ await(用於支持異步 function 的環境)
異步接口請求的時候可使用 supertest提供http請求方案

1.使用回調方法

//異步接口請求測試

it('異步請求應該返回一個對象', function(done){
  supertest
    .get('https://api.github.com')
    .end(function(err, res){
      expect(res).to.be.an('object');
      done();
    });
});

如上使用 supertest方法能很好的進行異步回調處理api

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

function asyncFn (string, callback){
    var withCallback \=  typeof callback \===  'function';
    try  {  
        let hash = string;
        withCallback &&  callback(null, hash);  
    }  catch  (e)  {  
        if  (withCallback)  {  
            callback(e);  
        }  else  {  
            throw e;  
        }  
    }
}


 describe('# asyncFn()', function() {
    it('異步函數回調處理', function(done) {
      asyncFn('test',function(err,str){
        // 使用 error 來調用 done() 回調函數,  
        // 來終止帶有錯誤的測試
        if(err) return  done(err);
        
        expect(str).to.be.equal('test');
        // 調用 done() 回調函數來終止測試  done();
      })
    })
  })

如上使用回調函數處理異步代碼promise

2.使用 Promise

function promiseAsyncFn (str){
    return new Promise((reolve,reject)=>{
        asyncFn(string,  (err, str) =>  {  
            return err ?  reject(err)  :  resolve(str); 
        })
    })
}


 describe('# asyncFn()', function() {
    it('異步函數回調處理 resolve', function(done) {
      return  promiseAsyncFn('test').then(function()=>{
        expect(str).to.be.equal('test');
        done()
      })
    })
    
    it('異步函數回調處理 reject', function(done) {
      return  promiseAsyncFn('test').catch(
      function(err)=>{
        expect(function(){throw err}).to.throw(TypeError,  'The "data" argument must be one of type string, TypedArray, or DataView. Received type number');
        done(err)
      })
    })
 })

3.使用 async /await

describe('# asyncFn()', function() {
    it('異步函數回調處理 resolve', async function(done) {
       let data = await promiseAsyncFn('test')
        expect(data).to.be.equal('test');
        done()
    })
    
    it('異步函數回調處理 reject',async function(done) {
        await promiseAsyncFn('test').catch(
            function(err)=>{
                expect(function(){throw err}).to.throw(TypeError,  'The "data" argument must be one of type string, TypedArray, or DataView. Received type number');
            done(err)
      })
    })
 })

看完上面三種異步代碼的處理方法熟悉js的小夥伴要開始扔磚了,和JavaScript好像同樣,我寫完以後發現確實同樣,可是咱們不能大意其中done()的特性!瀏覽器

done()使用的主意事項
  1. it() 用例中必須調用done()結束測試,不然測試將一直運行直到超時。
  2. it() 中done()不能被調用屢次,有且僅有一次
  3. done() 參數能夠是錯誤,並可顯示指定錯誤

斷言庫chai

細心的同窗應該發現了每一個it用例彙總總會出現個 expect,這是配合mocha處理錯誤判斷異常的斷言庫 chai模塊中的一個方法,其餘如 Expect.js, Should.js;mocha中也擁有內置的 assert模塊可是咱們優先使用開發中更加經常使用的以及功能強大的chai
npm i --save-dev chai

expect風格

經常使用的expect斷言風格以下框架

// 相等或不相等
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斷言的寫法都是同樣的。頭部是expect方法,尾部是斷言方法,好比equala/anokmatch等。二者之間使用toto.be鏈接。

Assert風格

var assert = require('chai').assert;  
var numbers = [1,  2,  3,  4,  5];

assert.isArray(numbers, 'is array of numbers');  
assert.include(numbers, 2, 'array contains 2');  
assert.lengthOf(numbers, 5, 'array contains 5 numbers');

Should 風格

var should = require('chai').should();
var numbers = [1,  2,  3,  4,  5];
numbers.should.be.an('array').that.includes(2);  
numbers.should.have.lengthOf(5);

mocha中的hooks

Mocha 提供了建立測試 hooks 的功能,hooks 基本上是配置爲在測試以前或以後運行的邏輯。它們對於設置測試的前提條件或者在測試後清理資源頗有用。使用默認的 BDD 接口,Mocha 提供了四個 hooks:

  1. before()- 在塊中的第一個測試用例以前運行一次
  2. beforeEach()- 在每一個測試用例前運行
  3. afterEach()- 在每一個測試用例以後運行
  4. after()- 在塊中的最後一個測試用例以後運行一次

執行順序以下

before()  ->  beforeEach()  ->  test()  ->  afterEach()  ->  after()

建立一個簡單的hooks

describe('hooks', function() {

  before(function() {
    // 在本區塊的全部測試用例以前執行
  });

  after(function() {
    // 在本區塊的全部測試用例以後執行
  });

  beforeEach(function() {
    // 在本區塊的每一個測試用例以前執行
  });

  afterEach(function() {
    // 在本區塊的每一個測試用例以後執行
  });

  // test cases
});

騷操做

mocha 同時具有一些自身可配參數,好比修改用例超時時間、遞歸執行test文件夾下的全部路徑、使用 skip()、only()控制it、describe是否執行...

1. 包含/排除測試skip()、only()

// 此測試套件中的測試將運行
describe.only('#flattenArray()', function() {
  describe('#二級-1()', function() {
    it.only('should merge two arrays', function() {})
    it('should merge two arrays123123', function() {})
  })
  // it('should flatten array', function() {})
  describe.skip('#二級-2()', function() {
    it('should merge two arrays', function() {})
  })
})

// 此測試套件中的測試沒法運行
describe('#mergeArray()', function() {
  it('should merge two arrays', function() {})
})

結果以下:
image.png
only會影響測試套件、測試用例
如上在第一個describe使用了only以後相鄰的測試用例則被規避,不執行了

2. 重複測試
mocha 提供了 this.retries () 函數,該函數可讓您指定容許重試的次數。對於每次重試,Mocha 都會運行從新 beforeEach () 和 afterEach () hooks,但不會從新運行 before () 和 after () 鉤子。

// this.retries() 函數的用法
describe('test medium site', function() {
  // 這個套件中全部失敗的測試都會重試2次
  this.retries(2)
  it('should load medium homepage', function() {
    // 這個測試用例測試失敗後,會被重複5次
    this.retries(5)

    cy.visit('https://medium.com')
  })
})

3. 慢速定義
this.slow(value) 設定一個值界定當前測試用例超過這個臨界點是慢的

describe('slow test', function() {
  // 在一秒以後,測試將被認爲是緩慢的
  this.slow(1000)

  // 在指定的一秒鐘以後完成
  it('should be complete in a second', function(done) {
    setTimeout(done, 1500)
  })

  // 當即完成
  it('should be complete instantly', function() {})
})

4. 超時定義
this.timeout(value) 可設置一個數值告訴mocha最大等待時間,超出時間則超時,mocha默認超時時間是2S

describe('some time-consuming operation', function() {
  // 給這個測試用例設置 5 秒的超時時間
  this.timeout(5000)

  before('some long step', function(done) {
    // 設置 hooks 的超時時間
    this.timeout(2500)

    setTimeout(done, 2250)
  })

  it('should take less than 200ms', function(done) {
    // 爲當前測試用例設置超時時間
    this.timeout(200)

    setTimeout(done, 150)
  })
})

MOCHA CLI

mocha --watch 
// 標誌指示 Mocha 監聽測試文件的變更並從新運行測試。這對於在開發過程當中編寫測試很是有用。


mocha --async-only
// 標誌強制全部測試必須傳遞迴調函數或返回一個 promise,從而異步執行。對於未指定回調函數或者未返回 promise 的測試將被標記爲失敗


mocha --bail
// 標誌指定只要有一個測試用例沒有經過,就中止後面全部的測試用例。這對於持續繼承頗有用

mocha -t 3000
// 等價於`this.timeout()`方法,這個做用於全局

mocha --no-timeouts
// 等價於`--timeout 0``this.timeout(0)`,表示~~~~禁用超時限制

mocha --slow 100
// 默認的閾值是 75ms,標記速率的閾值

mocha --require should
// 標誌容許您導入在測試文件中使用的模塊 / 庫(例如斷言庫),而不是在代碼中手動調用 require ()。

mocha -R list
// 能夠指定測試報告的格式。默認的是`spec`格式。Mocha 還容許您使用此標誌指定第三方報告格式。

mocha 還能使用將測試報告跑在瀏覽器中,結合一些插件能提升必定效率;
說到這裏,我認爲一個成熟的開發團隊,應該是有必要也是必須有意識的書寫測試用例,爲後人以及新需求搭建穩固的基礎,啦啦啦

歡迎來到本身的blog小站留言

相關文章
相關標籤/搜索