成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

如何使用 mocha 和 sinon 集成單元測(cè)試--單元測(cè)試示例及分析(上)

Caicloud / 1034人閱讀

摘要:使用集成單元測(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 命令

命令

  "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è)試文件

參數(shù)解析

--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è)面

測(cè)試含有jQuery的代碼 初始化Jquery環(huán)境
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事件

測(cè)試post事件

由于初始化jquery環(huán)境比較通用,我們把它放到工具類去引用

utils.js
const 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)行一次

測(cè)試ajax demo3.js
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.js
export 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.js
const 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án)境編寫單元測(cè)試

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

如圖


最佳實(shí)踐總結(jié)

文件名以及路徑的定義如下,這樣定義規(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

相關(guān)文章

  • 【Node Hero】9. Node.js 單元測(cè)試

    摘要:基本上,測(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...

    104828720 評(píng)論0 收藏0
  • mocha、chai、sinonistanbul實(shí)現(xiàn)100%單元測(cè)試覆蓋率

    摘要:加上測(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è)試覆蓋率衡量...

    Yuanf 評(píng)論0 收藏0
  • 前端單元測(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è)試...

    isLishude 評(píng)論0 收藏0
  • 使用karma+mocha+chai+sinon+@vue/test-utils為你的組件庫(kù)增加單元

    摘要:但是,項(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ú)意義...

    happen 評(píng)論0 收藏0
  • Ajax單元測(cè)試傻瓜教程

    摘要:原文出處單元測(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ù)器的連接總是工作...

    30e8336b8229 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<