摘要:使用集成單元測(cè)試上項(xiàng)目地址安裝依賴測(cè)試框架可視化報(bào)表覆蓋率替換依賴斷言命令命令命令執(zhí)行單元測(cè)試,并打開(kāi)測(cè)試報(bào)告頁(yè)面和覆蓋率頁(yè)面執(zhí)行生成單元測(cè)試覆蓋率并打開(kāi)執(zhí)行單個(gè)單元測(cè)試文
使用 mocha 集成單元測(cè)試(上)
項(xiàng)目地址: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:測(cè)試框架
mochawesome:可視化報(bào)表
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 命令:執(zhí)行單元測(cè)試,并打開(kāi)測(cè)試報(bào)告頁(yè)面和覆蓋率頁(yè)面
test:cover 執(zhí)行生成單元測(cè)試覆蓋率并打開(kāi)
test:s 執(zhí)行單個(gè)單元測(cè)試文件
--timeout 5000 超時(shí)設(shè)置
--recursive 包含子目錄
--reporter mochawesome 通過(guò)mochawesome生成報(bào)表
--require babel-core/register 通過(guò)babel轉(zhuǎn)譯es6語(yǔ)法
tests/src 單元測(cè)試目錄路徑
open mochawesome-report/mochawesome.html 打開(kāi)頁(yè)面
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");測(cè)試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); }); });運(yùn)行結(jié)果
以上測(cè)試了,點(diǎn)擊元素時(shí),給該元素添加一個(gè)‘hide’類的方法
模擬jquery環(huán)境和觸發(fā)click事件
由于初始化jquery環(huán)境比較通用,我們把它放到工具類去引用
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"); }
因?yàn)閚ode環(huán)境中,require會(huì)有緩存,導(dǎo)致不同的單元測(cè)試間的初始環(huán)境不一致,需要手動(dòng)清除緩存
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()操作 將會(huì)復(fù)原被替換的對(duì)象
mocha 有四個(gè)鉤子方法
before 在所有的單元測(cè)試運(yùn)行前運(yùn)行一次
after 在所有的單元測(cè)試運(yùn)行結(jié)束運(yùn)行一次
beforeEach 在每一個(gè)的單元測(cè)試運(yùn)行前運(yùn)行一次
afterEach 在每一個(gè)的單元測(cè)試運(yùn)行后運(yùn)行一次
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斷言參數(shù),該方法可以斷言傳入的參數(shù)是否正確,不需要傳入所有的參數(shù)
測(cè)試異步代碼 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告知這個(gè)單元測(cè)試結(jié)束了,保證不影響其他單元測(cè)試 done(); }; setTimeout(test, 1001); }); it("test by sinon", function() { //當(dāng)利用了useFakeTimers后,事件將會(huì)停止 clock = sinon.useFakeTimers(); let $demo = $("#demo4"); //運(yùn)行demo4前,元素還是顯示的 assert.equal($demo.css("display"), "block"); demo4(); //運(yùn)行demo4完,元素隱藏了 assert.equal($demo.css("display"), "none"); //時(shí)間穿梭101ms秒,定時(shí)器代碼還未執(zhí)行,所以元素還是隱藏的 clock.tick(101); assert.equal($demo.css("display"), "none"); //時(shí)間再穿梭900ms秒,就到達(dá)了1001ms后,定時(shí)器代碼執(zhí)行了,所以元素現(xiàn)在顯示了 clock.tick(900); assert.equal($demo.css("display"), "block"); //恢復(fù)時(shí)間 clock.restore(); }); });
第一個(gè)單元測(cè)試?yán)昧?setTimeout 去測(cè)試異步代碼
第二個(gè)單元測(cè)試?yán)昧?sinon 的時(shí)空穿梭器去測(cè)試異步代碼
結(jié)果如圖所示
第一個(gè)單元測(cè)試花了1035ms
而第二個(gè)單元測(cè)試幾乎沒(méi)有花費(fèi)多少時(shí)間
所以異步代碼編寫單元測(cè)試時(shí),第二個(gè)單元測(cè)試寫法更優(yōu)
需要測(cè)試的代碼包含其他負(fù)責(zé)業(yè)務(wù)邏輯時(shí) 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(); }); });
此時(shí)demo5依賴其他模塊,我們就可以替換demo5require的方法,并指定返回值,這樣就不用關(guān)系依賴的模塊做了什么業(yè)務(wù)。
測(cè)試結(jié)束,復(fù)原被替換的對(duì)象
webpack中會(huì)有設(shè)置別名的情況,這樣單元測(cè)試有可能引入的模塊的路徑有誤,這里我們可以使用babel-plugin-module-resolver進(jìn)行別名的替換
.babelrc{ "presets": ["es2015"], "plugins": [ ["module-resolver", { "root": ["./"], "alias": { "common":"" } }] ] }運(yùn)行結(jié)果
執(zhí)行命令
npm run test
如圖
文件名以及路徑的定義如下,這樣定義規(guī)范了路徑的書寫,便于文件的查找
/a/b/c/demo.js //待測(cè)試文件 /tests/a/b/c/test.demo.js //單元測(cè)試文件
一個(gè)單元測(cè)試文件測(cè)試一個(gè)js文件
一個(gè)describe測(cè)試一個(gè)方法
一個(gè)it 測(cè)試一個(gè)方法中一個(gè)邏輯,這樣保證一個(gè)測(cè)試只驗(yàn)證一個(gè)行為
使用sinon隔離外部調(diào)用
使用before或beforeEach 初始環(huán)境
使用after或afterEach 清空或還原環(huán)境,不同單元測(cè)試互不影響,狀態(tài)不共享
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/93807.html
摘要:基本上,測(cè)試金字塔描述你應(yīng)該編寫單元測(cè)試集成測(cè)試和端到端測(cè)試。集成測(cè)試要比端到端測(cè)試多,單元測(cè)試甚至要更多一些。應(yīng)用程序單元測(cè)試編寫單元測(cè)試,是為了看看給定的模塊單元是否工作。 本文轉(zhuǎn)載自:眾成翻譯譯者:網(wǎng)絡(luò)埋伏紀(jì)事鏈接:http://www.zcfy.cc/article/1754原文:https://blog.risingstack.com/node-hero-node-js-un...
摘要:加上測(cè)試覆蓋率檢查,就能夠提供足夠的信息,來(lái)斷言代碼的行為是否符合期望。測(cè)試的相關(guān)技術(shù)是程序的代碼覆蓋率工具,以土耳其最大城市伊斯坦布爾命名。 showImg(https://segmentfault.com/img/remote/1460000010260434); 敏捷軟件開(kāi)發(fā)中,最重要實(shí)踐的就是測(cè)試驅(qū)動(dòng)開(kāi)發(fā),在單元測(cè)試層面,我們?cè)囍鴮?shí)現(xiàn)一個(gè)重要的指標(biāo)就是測(cè)試覆蓋率。測(cè)試覆蓋率衡量...
摘要:本文只討論單測(cè)的范疇,對(duì)集成測(cè)試有興趣的話,可以看下的集成測(cè)試代碼。前端單測(cè)現(xiàn)狀測(cè)試本質(zhì)上就是假定一個(gè)輸入,然后判斷得到預(yù)期的輸出。 原文發(fā)于我的博客:https://github.com/hwen/blogS... 要不要寫單測(cè)? 關(guān)于這個(gè) cnode 上就有個(gè)很有意思的討論 做個(gè)調(diào)查,你的 Node 應(yīng)用有寫單測(cè)嗎? 看完這個(gè)應(yīng)該會(huì)有結(jié)論?如果沒(méi)有,就回帖跟別人探討下~ 測(cè)試 測(cè)試...
摘要:但是,項(xiàng)目中的一些公共封裝,比如公共的組件公用的功能模塊等是可以使用單元測(cè)試的。因此特為組件庫(kù)引入單元測(cè)試,目的在于能減少組件的,避免重復(fù)的發(fā)布不必要的包。 項(xiàng)目github地址:https://github.com/yuanalina/installAsRequired這里必須要提前說(shuō)明,前端項(xiàng)目的單元測(cè)試不是必須的,特別是業(yè)務(wù)型項(xiàng)目,增加單元測(cè)試反而會(huì)成為累贅,增加開(kāi)發(fā)成本且無(wú)意義...
摘要:原文出處單元測(cè)試傻瓜教程請(qǐng)求經(jīng)常容易發(fā)生錯(cuò)誤,客戶端發(fā)送的數(shù)據(jù)出問(wèn)題,服務(wù)器端返回的數(shù)據(jù)有誤都會(huì)導(dǎo)致請(qǐng)求錯(cuò)誤。設(shè)置在我們開(kāi)始單元測(cè)試之前,我們需要安裝幾個(gè)必須的工具。我們將用它來(lái)向你們展示如何對(duì)進(jìn)行單元測(cè)試。 原文出處 :AJAX單元測(cè)試傻瓜教程 Ajax 請(qǐng)求經(jīng)常容易發(fā)生錯(cuò)誤,客戶端發(fā)送的數(shù)據(jù)出問(wèn)題,服務(wù)器端返回的數(shù)據(jù)有誤都會(huì)導(dǎo)致 Ajax 請(qǐng)求錯(cuò)誤。你不能保證與服務(wù)器的連接總是工作...
閱讀 3167·2021-11-22 09:34
閱讀 2806·2021-09-22 15:28
閱讀 835·2021-09-10 10:51
閱讀 1865·2019-08-30 14:22
閱讀 2332·2019-08-30 14:17
閱讀 2746·2019-08-30 11:01
閱讀 2306·2019-08-29 17:19
閱讀 3674·2019-08-29 13:17