爲何要寫單元測試css
當你每一個方法都寫了單元測試的時候,你每個改動都會影響相應的單元測試,這樣你不用費盡心思的考慮哪裏會有影響,特別是複雜項目或非核心功能(不易被測試到),從而致使bug的產生。html
當你的代碼不可測試的時候,就得考慮你的代碼是否須要重構的。好的代碼應該是職責分明且單一,顆粒度小且易於測試。前端
當你重構時,特別是大範圍的重構,你就有勇氣和信心了。node
那麼單元測試須要有那些要素呢jquery
安裝es6
npm install mocha
package.jsongithub
"scripts": { "test": "mocha --recursive --require babel-core/register tests/Js/test" }
mocha默認會找到項目的根目錄下的 test目錄,可是不少人項目目錄中單元測試目錄並非test,而是在/tests/Js/test中,在scripts中,後面加上單元測試路徑,就能夠修改默認地址npm
recursive參數表明查找 目錄的全部子目錄下的單元測試,不然只會查找當前目錄下的單元測試json
node中並不支持某些es6語法,須要經過babel編譯,因此須要添加 --require babel-core/register
同時須要在項目根目錄添加.babelrc文件
{ "presets": [ "es2015" ] }
如今在 tests/Js/test目錄下建立一個文件 test.js
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); }); }); });
命令行中執行 npm run test 結果以下
以上咱們知道了如何引入前端測試框架,es6語法問題,執行路徑問題
有時候命令行的結果看着不明瞭,想要輸出測試報告呢
這時候可使用 mochawesome
安裝
npm install --save-dev mochawesome
package.json
"scripts": { "test": "mocha --recursive --reporter mochawesome --require babel-core/register tests/Js/test" }
在scripts命令中,添加 --reporter mochawesome
執行命令
npm run test
結果如圖,將會生成 html文件和json文件
"代碼覆蓋率"(code coverage)。它有四個測量維度。
那麼如何知道每一個js的覆蓋率呢
這時候用到了 istanbul 和 babel-istanbul
安裝
npm install istanbul npm install babel-istanbul
package.json
"scripts": { "test:cover": "babel-node ./node_modules/.bin/babel-istanbul cover _mocha -- tests/Js/* -R spec --recursive }
istanbul他也是不支持一些es6語法的,因此也須要babel轉譯
使用cover參數結合mocha,--表明後面參數傳遞給mocha
執行命令
npm run test:cover
結果如圖
Mocha自己不帶斷言庫,因此必須先引入斷言庫。
咱們這裏使用 chai
npm install chai
chaijs有三種斷言風格,詳細查看官網
jquery做爲大部分都在使用的庫,那麼如何對這類的代碼進行單元測試呢
好比如下代碼
hide-element.js
export const hideElement = ($element) => { $element.on('click', '.hide', function() { $(this).hide(); }); };
首先node環境和瀏覽器環境是不同的,因此咱們跑這類的單元測試就須要模擬出瀏覽器環境
咱們須要安裝 jsdom
npm install jsdom
test.js 以下
const assert = require('chai').assert; const { hideElement } = require('xxxx/hide-element.js'); describe('test:hide-element.js', function(done) { before(function() { let { JSDOM } = require('jsdom'); let dom = new JSDOM(`<!DOCTYPE html><html><body><div class="hide"></div></body></html>`,{ url: 'http://127.0.0.1/', referrer: 'http://127.0.0.1/', contentType: 'text/html', userAgent: 'Mellblomenator/9000', includeNodeLocations: true, }); global.window = dom.window; global.$ = require('jquery'); hideElement($('body')); }); it('click event', function() { $('body').find('.hide').trigger('click'); assert.equal($('.hide').css('display'), 'none'); }); });
須要測試的代碼中依賴了其餘的模塊時,爲了測試須要測試的代碼,而不去關心依賴的模塊,這時候咱們須要 sinon 去mock掉相關依賴
demo.js
import api from 'api'; export const demo = (arg) => { if (arg == 1) { return api.get({ params: params }); } return 'ok'; };
test.js
import { demo } from 'xx/demo.js'; const assert = require('chai').assert; const sinon = require('sinon'); import api from 'api'; describe('demo', function() { it('demo(1)', function() { //mock api的get方法,而且指定返回值爲 'N' let apiGet = sinon.stub(api, 'get').returns('N'); let expectedParams = {params: 'yes'}; let res = demo(1); //automate clean-up,防止影響其餘單元測試 apiGet.restore(); //斷言調用api.get 是傳入的參數爲 {params: 'yes'} sinon.assert.calledWith(apiGet, expectedParams); assert.equal(res, 'N'); }); });