摘要:使用集成單元測試上項目地址安裝依賴測試框架可視化報表覆蓋率替換依賴斷言命令命令命令執行單元測試,并打開測試報告頁面和覆蓋率頁面執行生成單元測試覆蓋率并打開執行單個單元測試文
使用 mocha 集成單元測試(上)
項目地址:https://github.com/Jay-tian/j...安裝依賴
yarn add jquery mocha mochawesome istanbul sinon chai jsdom decache babel-cli babel-core babel-preset-es2015 babel-plugin-module-resolver babel-istanbul
mocha:測試框架
mochawesome:可視化報表
istanbul:覆蓋率
sinon:替換依賴
chai:斷言
命令
"scripts": { "test": "mocha --timeout 5000 --recursive --reporter mochawesome --require babel-core/register tests/src && open mochawesome-report/mochawesome.html && npm run test:cover", "test:cover": "babel-node ./node_modules/.bin/babel-istanbul cover _mocha -- tests/src/* -R spec --recursive && open coverage/lcov-report/index.html", "test:s": "mocha --recursive --require babel-core/register --timeout 5000" }
test 命令:執行單元測試,并打開測試報告頁面和覆蓋率頁面
test:cover 執行生成單元測試覆蓋率并打開
test:s 執行單個單元測試文件
--timeout 5000 超時設置
--recursive 包含子目錄
--reporter mochawesome 通過mochawesome生成報表
--require babel-core/register 通過babel轉譯es6語法
tests/src 單元測試目錄路徑
open mochawesome-report/mochawesome.html 打開頁面
let { JSDOM } = require("jsdom"); let dom = new JSDOM(``,{ 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");測試click事件
const { demo1 } = require("../../src/demo1.js"); const assert = require("chai").assert; describe("demo1", function() { it("jquery click test", function() { demo1($("body")); assert.equal($("body").hasClass("hide"), false); $("body").trigger("click"); assert.equal($("body").hasClass("hide"), true); }); });運行結果
以上測試了,點擊元素時,給該元素添加一個‘hide’類的方法
模擬jquery環境和觸發click事件
由于初始化jquery環境比較通用,我們把它放到工具類去引用
utils.jsconst decache = require("decache"); let { JSDOM } = require("jsdom"); exports.initJquery = function(html, params = {}){ params = Object.assign({ url: "http://127.0.0.1", referrer: "http://127.0.0.1", contentType: "text/html", userAgent: "Mellblomenator/9000", includeNodeLocations: true, }, params); let dom = new JSDOM(`${html}`, params); global.window = dom.window; decache("jquery"); global.$ = require("jquery"); }
因為node環境中,require會有緩存,導致不同的單元測試間的初始環境不一致,需要手動清除緩存
decache("jquery");test.demo2.js
import post from "../../src/demo2.js"; const utils = require("./../utils"); const sinon = require("sinon"); require("./../utils"); describe("demo2", function() { before(function() { utils.initJquery(""); }); it("jquery post", function() { let stubPost = sinon.stub($, "post"); let expectedUrl = "/demo2"; let expectedParams = {"a": "abc"}; post(); sinon.assert.calledWith(stubPost, expectedUrl, expectedParams); stubPost.restore(); }); });
restore()操作 將會復原被替換的對象
mocha 有四個鉤子方法
before 在所有的單元測試運行前運行一次
after 在所有的單元測試運行結束運行一次
beforeEach 在每一個的單元測試運行前運行一次
afterEach 在每一個的單元測試運行后運行一次
export default function() { $.ajax({ type: "GET", url: null, async: true, promise: true, dataType: "json", beforeSend(request) { } }); }test.demo3.js
import ajax from "../../src/demo3.js"; const utils = require("./../utils"); const sinon = require("sinon"); require("./../utils"); describe("demo3", function() { before(function() { utils.initJquery(""); }); it("jquery ajax", function() { let stubAjax = sinon.stub($, "ajax"); let expectedParams = { type: "GET", url: null, async: true, promise: true, dataType: "json" }; ajax(); sinon.assert.calledWithMatch(stubAjax, expectedParams); stubAjax.restore(); }); });
這里我們使用calledWithMatch斷言參數,該方法可以斷言傳入的參數是否正確,不需要傳入所有的參數
測試異步代碼 demoe4.jsexport default function() { $("#demo4").hide(); setTimeout( function(){ $("#demo4").show(); }, 1000); }
import demo4 from "../../src/demo4.js"; const utils = require("./../utils"); const sinon = require("sinon"); const assert = require("chai").assert; require("./../utils"); describe("asynchronous code", function() { let clock; before(function () { utils.initJquery(""); }); it("test by setTimeout", function(done) { let $demo = $("#demo4"); demo4(); assert.equal($demo.css("display"), "none"); let test = function() { assert.equal($demo.css("display"), "block"); // 這里的done告知這個單元測試結束了,保證不影響其他單元測試 done(); }; setTimeout(test, 1001); }); it("test by sinon", function() { //當利用了useFakeTimers后,事件將會停止 clock = sinon.useFakeTimers(); let $demo = $("#demo4"); //運行demo4前,元素還是顯示的 assert.equal($demo.css("display"), "block"); demo4(); //運行demo4完,元素隱藏了 assert.equal($demo.css("display"), "none"); //時間穿梭101ms秒,定時器代碼還未執行,所以元素還是隱藏的 clock.tick(101); assert.equal($demo.css("display"), "none"); //時間再穿梭900ms秒,就到達了1001ms后,定時器代碼執行了,所以元素現在顯示了 clock.tick(900); assert.equal($demo.css("display"), "block"); //恢復時間 clock.restore(); }); });
第一個單元測試利用了 setTimeout 去測試異步代碼
第二個單元測試利用了 sinon 的時空穿梭器去測試異步代碼
結果如圖所示
第一個單元測試花了1035ms
而第二個單元測試幾乎沒有花費多少時間
所以異步代碼編寫單元測試時,第二個單元測試寫法更優
需要測試的代碼包含其他負責業務邏輯時 demo5.jsconst demo5require = require("./demo5.require.js"); export default function() { if(demo5require.a() == "a") { return 1; } else { return 2; } }test.demo5.js
import demo5 from "../../src/demo5.js"; const utils = require("./../utils"); const sinon = require("sinon"); const assert = require("chai").assert; describe("demo5", function() { before(function () { utils.initJquery(""); }); it("test", function() { assert.equal(demo5(), 1); const demo5require = require("../../src/demo5.require.js"); let stub = sinon.stub(demo5require, "a").returns("b"); assert.equal(demo5(), 2); stub.restore(); }); });
此時demo5依賴其他模塊,我們就可以替換demo5require的方法,并指定返回值,這樣就不用關系依賴的模塊做了什么業務。
測試結束,復原被替換的對象
webpack中會有設置別名的情況,這樣單元測試有可能引入的模塊的路徑有誤,這里我們可以使用babel-plugin-module-resolver進行別名的替換
.babelrc{ "presets": ["es2015"], "plugins": [ ["module-resolver", { "root": ["./"], "alias": { "common":"" } }] ] }運行結果
執行命令
npm run test
如圖
文件名以及路徑的定義如下,這樣定義規范了路徑的書寫,便于文件的查找
/a/b/c/demo.js //待測試文件 /tests/a/b/c/test.demo.js //單元測試文件
一個單元測試文件測試一個js文件
一個describe測試一個方法
一個it 測試一個方法中一個邏輯,這樣保證一個測試只驗證一個行為
使用sinon隔離外部調用
使用before或beforeEach 初始環境
使用after或afterEach 清空或還原環境,不同單元測試互不影響,狀態不共享
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93807.html
摘要:基本上,測試金字塔描述你應該編寫單元測試集成測試和端到端測試。集成測試要比端到端測試多,單元測試甚至要更多一些。應用程序單元測試編寫單元測試,是為了看看給定的模塊單元是否工作。 本文轉載自:眾成翻譯譯者:網絡埋伏紀事鏈接:http://www.zcfy.cc/article/1754原文:https://blog.risingstack.com/node-hero-node-js-un...
摘要:加上測試覆蓋率檢查,就能夠提供足夠的信息,來斷言代碼的行為是否符合期望。測試的相關技術是程序的代碼覆蓋率工具,以土耳其最大城市伊斯坦布爾命名。 showImg(https://segmentfault.com/img/remote/1460000010260434); 敏捷軟件開發中,最重要實踐的就是測試驅動開發,在單元測試層面,我們試著實現一個重要的指標就是測試覆蓋率。測試覆蓋率衡量...
摘要:但是,項目中的一些公共封裝,比如公共的組件公用的功能模塊等是可以使用單元測試的。因此特為組件庫引入單元測試,目的在于能減少組件的,避免重復的發布不必要的包。 項目github地址:https://github.com/yuanalina/installAsRequired這里必須要提前說明,前端項目的單元測試不是必須的,特別是業務型項目,增加單元測試反而會成為累贅,增加開發成本且無意義...
摘要:原文出處單元測試傻瓜教程請求經常容易發生錯誤,客戶端發送的數據出問題,服務器端返回的數據有誤都會導致請求錯誤。設置在我們開始單元測試之前,我們需要安裝幾個必須的工具。我們將用它來向你們展示如何對進行單元測試。 原文出處 :AJAX單元測試傻瓜教程 Ajax 請求經常容易發生錯誤,客戶端發送的數據出問題,服務器端返回的數據有誤都會導致 Ajax 請求錯誤。你不能保證與服務器的連接總是工作...
閱讀 3162·2021-11-22 09:34
閱讀 2800·2021-09-22 15:28
閱讀 827·2021-09-10 10:51
閱讀 1858·2019-08-30 14:22
閱讀 2325·2019-08-30 14:17
閱讀 2739·2019-08-30 11:01
閱讀 2301·2019-08-29 17:19
閱讀 3666·2019-08-29 13:17