寫這篇文章的初衷是,有個朋友來找我寫單元測試...本身懵逼了,開發了這麼久,還真沒有好好寫過測試用例,而後就和我朋友掰扯,個人論點是,前端開發不必寫單元測試,通常公司都有端對端測試就是點點點...開發人員一旦書寫測試用例,無形增長人員的開銷,或者是延緩迭代速率...他一句話,上頭須要...兩人啞口無言乘着空閒的時候就查閱了各類資料
思考一:是否有必要單元測試?(團隊是否擁有測試團隊的兩種狀況)
思考二:實在要編寫單元測試如何編寫優雅的測試用例模塊?javascript你們有空不妨告訴我大家公司會用到單元測試麼?有必要麼?前端
下面先自我歸總下
Mocha
單元測試的基礎api使用和注意點java
字如其名,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請求方案
//異步接口請求測試 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
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) }) }) })
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()
使用的主意事項細心的同窗應該發現了每一個it用例彙總總會出現個expect
,這是配合mocha處理錯誤判斷異常的斷言庫chai
模塊中的一個方法,其餘如 Expect.js, Should.js;mocha中也擁有內置的assert
模塊可是咱們優先使用開發中更加經常使用的以及功能強大的chai
npm i --save-dev chai
經常使用的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
方法,尾部是斷言方法,好比equal
、a
/an
、ok
、match
等。二者之間使用to
或to.be
鏈接。
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');
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 的功能,hooks 基本上是配置爲在測試以前或以後運行的邏輯。它們對於設置測試的前提條件或者在測試後清理資源頗有用。使用默認的 BDD 接口,Mocha 提供了四個 hooks:
before()
- 在塊中的第一個測試用例以前運行一次beforeEach()
- 在每一個測試用例前運行afterEach()
- 在每一個測試用例以後運行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() {}) })
結果以下:
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 --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小站留言