學習單元測試的時候接觸了不少概念karma、mocha、Jesmine、chai、expect、assert、should、sinon等,容易混亂,在此作個梳理。javascript
入門文章參考:www.ruanyifeng.com/blog/2015/1…html
Mocha是一個經常使用的JS測試框架,能夠在瀏覽器和Nodejs環境使用。Mocha不帶斷言須要和斷言庫結合使用。項目中使用的也是Mocha+chai+sinon的結合。例如給ndfront組件寫的單元測試,詳情查看github倉庫:github.com/baihexx/ndf…前端
特色:靈活,可擴展性好,可配合不一樣的斷言庫使用,可是自身集成度不高java
Jesmine也是經常使用的測試框架,項目中沒有用這個。node
特色:內置斷言庫,集成度高,方便支持異步測試,可是靈活性差,斷言風格單一git
assert模塊是Node的內置模塊,用於斷言。
官方API
經常使用APIes6
eg:github
var assert = requier('assert')
describe('desc1', function() {
it('desc2', function() {
assert(a === 1, '預期a的值是1')
})
})
複製代碼
should.js是個第三方斷言庫,常和Mocha聯合使用。後端
var should = require('should');
(5).should.be.exactly(5).and.be.a.Number();
var a = null
a.should.not.be.ok() // 報錯
複製代碼
var should = require('should/as-function');
var a = null
should(a).not.be.ok() // pass
should(10).be.exactly(5).and.be.a.Number();
複製代碼
官網API: 安裝方法等查看官網
Chai是個斷言庫,常和Mocha結合使用。他有多種斷言風格(assertion style):assert, expect, should
var assert = requier('chai').assert
assert.notEqual(3, 4, 'these numbers are not equal')
複製代碼
var expect = requier('Chai').expect
expect([1, 2, 3]).to.be.an('array').that.includes(2)
複製代碼
var should = require('chai').should() //actually call the function
var foo = 'bar'
foo.should.be.a('string')
foo.should.equal('bar')
foo.should.have.lengthOf(3)
複製代碼
(注意should對IE兼容性很差)
定義:A simple tool that allows you to execute JavaScript code in multiple real browsers. The main purpose of Karma is to make your test-driven development easy, fast, and fun.
Karma不是測試框架,也不是斷言庫,他會開啓一個HTTP服務,將測試文件生成一個Html文件,在瀏覽器內運行、調試。Karma不指定測試框架,經過插件和Mocha、Jesmine、QUnit均可以結合使用。
配置項較多,根據官網說明配置,並不難。
測試覆蓋率:根據提示安裝、配置便可生成覆蓋率報告
爲何須要Sinon?在作單元測試的時候,咱們會發現咱們要測試的方法會引用不少外部依賴的對象,好比:(發送郵件,網絡通信,記錄Log, 文件系統之類的),而咱們無法控制這些外部依賴的對象。例如:前端項目一般是用Ajax去服務端請求數據,獲得數據以後作進一步的處理。可是作單元測試的時候一般不真的去服務端請求數據,不只麻煩,可能服務端接口還沒作好,這種不肯定的依賴使得測試變得複雜。因此咱們須要模擬這個請求數據的過程,Sinon用來解決這個問題。
Sinon的工做本質是「測試替身」,測試替身用來替換測試中的部分代碼,使得測試複雜代碼變得簡單。
Sinon提供了三個功能:示例講解看入門文章,再也不贅述
eg:admin/misc/user.js: user.getUser() (看不懂的隨便看看,這是實際的項目代碼單元測試)
user.getUser函數用來獲取用戶信息,用戶輸入工號後向服務端請求數據,咱們的測試用例重點在前端代碼,不該依賴服務端纔可測試,so 應該模擬ajax請求。
(1)nd-spa中ajax.js請求代碼以下:實際的請求函數爲:request.get, so應該mock request的get函數
(2)測試用例代碼以下:
用戶在前端頁面中一般是經過點擊鼠標期待某種效果,這個過程一般不作單元測試,由於複雜度較高,且頁面變更較快,性價比很低。可是項目中某些公共的業務組件,需求變更小,步驟相對簡單,但使用又很是多,例如social管理後臺中的搜索用戶admin/misc/user.js:user.autoComplete(),完整的過程是:用戶輸入用戶信息,而後選擇搜索到的匹配用戶,再點擊搜索到的用戶,該輸入框的值變爲選擇的用戶。這個過程如何編寫單元測試。
這裏涉及到多步驟的模擬。
(1)util.js 封裝多步執行函數
// utils.js
export function triggerHTMLEvents (target, event, process) {
const e = document.createEvent('HTMLEvents')
e.initEvent(event, true, true)
if (process) process(e)
target.dispatchEvent(e)
return e
}
export function triggerMouseEvents (target, event, process) {
const e = document.createEvent('MouseEvents')
e.initEvent(event, true, true)
if (process) process(e)
target.dispatchEvent(e)
return e
}
export function triggerUIEvents (target, event, process) {
const e = document.createEvent('UIEvents')
e.initEvent(event, true, true)
if (process) process(e)
target.dispatchEvent(e)
return e
}
// timeout: 執行間隔可設置
function createStep ({ step, timeout }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(step())
} catch (err) {
reject(err)
}
}, timeout || 0)
})
}
// 多步驟執行函數, 執行步驟封裝在arr數組中
export function runSteps (arr) {
if (arr.length === 0) {
return
}
let firstStep = createStep(arr[0])
const others = arr.splice(1)
others.forEach(item => {
firstStep = firstStep.then(() => {
return createStep(item)
})
})
}
複製代碼
(2)使用
過程當中遇到一個額外的問題,在此記錄下,備忘
(1)user中getUsers函數用到ucOrgId,查看代碼(var ucOrgId = auth.getAuth('uc_org_id') 且僅僅在登陸代碼中有auth.setAuth())可知該組織id信息必須有,可是測試頁面中沒有登陸,獲取不到該信息,so,造登陸數據,並設置到auth中。
(2)造登陸數據並設置到auth中時遇到一個問題:若在user.spec.js中 引入auth,then auth.setAuth(...), 結果不對
緣由:user.js中在開頭就執行了獲取ucOrgId的函數,咱們在測試代碼中先引入auth和user,這時ucOrgId已經獲取了,且是空值,即便假造登陸數據的函數寫在import user以前也是沒用,由於es6有提高的功能,老是先執行import,致使函數執行在後面,解決方法是:將設置登陸數據的函數寫在單獨的文件中,使用import的方法,且在user以前inport,就會達到先執行設置登陸數據的效果。(具體看代碼更清晰)
(import的提高再複習下)