摘要:現(xiàn)在,我們可以使用單元測(cè)試來(lái)提高自己的代碼質(zhì)量。它在單元測(cè)試的編寫中通常用來(lái)模擬等相關(guān)請(qǐng)求。通過(guò)這篇文章,你應(yīng)該學(xué)會(huì)了如何針對(duì)已有代碼從零開始編寫一套完整的單元測(cè)試用例。
概述
在日常的功能開發(fā)中,我們的代碼測(cè)試都依賴于自己或者QA進(jìn)行測(cè)試。這些操作不僅費(fèi)時(shí)費(fèi)力,而且還依賴開發(fā)者自身的驅(qū)動(dòng)。在開發(fā)一些第三方依賴的庫(kù)時(shí),我們也沒(méi)有辦法給第三方提供完整的代碼質(zhì)量報(bào)告。
現(xiàn)在,我們可以使用單元測(cè)試來(lái)提高自己的代碼質(zhì)量。下面,我將自己在使用Jest和Sinon.js配置和編寫單元測(cè)試中的收獲的經(jīng)驗(yàn)和踩到的坑進(jìn)行總結(jié),根據(jù)從零開始配置和編寫單元測(cè)試這一條線來(lái)進(jìn)行分享。
通過(guò)本文,你可以解決以下問(wèn)題:
Jest與Sinon.js是什么?
如何配置Jest與Sinon.js,從而編寫單元測(cè)試?
如何解決進(jìn)行單元測(cè)試中遇到的常見問(wèn)題?
Jest與Sinon.js是什么Jest是FaceBook推出的一個(gè)針對(duì)JavaScript進(jìn)行單元測(cè)試的庫(kù),它提供了斷言、函數(shù)模擬等API來(lái)對(duì)你自己編寫的業(yè)務(wù)邏輯代碼進(jìn)行測(cè)試后。
Sinon.js是一個(gè)用來(lái)做獨(dú)立測(cè)試和模擬的JavaScript庫(kù)。它在單元測(cè)試的編寫中通常用來(lái)模擬HTTP等相關(guān)請(qǐng)求。
為什么沒(méi)有用其他的單元測(cè)試框架在最開始的框架選擇中,我先嘗試了能夠并行測(cè)試,大大提高單元測(cè)試速度的ava框架。它能滿足日常的普通需求如utils工具集的測(cè)試,也能夠配置Sinon.js來(lái)進(jìn)行HTTP模擬測(cè)試。
但是,在處理webpack alias的問(wèn)題時(shí),通過(guò)官方issue中的極其復(fù)雜的配置也沒(méi)有能夠解決出現(xiàn)Cannot find module的問(wèn)題(其中一個(gè)解決此問(wèn)題的插件babel-plugin-webpack-loaders中竟然是推薦直接使用Jest,囧)。
而在Jest中,可以很方便的通過(guò)一些簡(jiǎn)單配置,就能夠識(shí)別在文件中使用的webpack alias,相關(guān)的具體方法將會(huì)在后面章節(jié)進(jìn)行具體描述。
而對(duì)于其他的測(cè)試框架如:Mocha或者Chai等,沒(méi)有進(jìn)行具體的了解,因此在這里不多做評(píng)價(jià)。
如何配置Jest與Sinon.js,從而編寫單元測(cè)試? Jest配置 安裝依賴包需要使用Jest,首先你需要進(jìn)行安裝,執(zhí)行以下命令:
npm install jest -D
如果你的項(xiàng)目中存在.babelrc文件(使用了babel 6)時(shí),不論你測(cè)試的代碼是否通過(guò)babel進(jìn)行編譯,你都需要安裝額外的幾個(gè)包:
npm install babel-jest babel-core regenerator-runtime -D
如果你使用的是babel 7,則需要安裝下面幾個(gè)包:
npm install babel-jest "babel-core@^7.0.0-0" @babel/core regenerator-runtime -Dpackage.json文件配置
在安裝完成依賴包以后,如果你有相關(guān)的jest配置項(xiàng)需要設(shè)置,你還可以在package.json文件中配置如下字段:
{ "jest": { } }
.babelrc文件只需要保存之前的配置,不需要做任何修改即可生效。
Sinon.js配置 依賴包安裝安裝配置完了Jest,讓我們來(lái)看下Sinon.js。需要使用Sinon.js,我們首先需要進(jìn)行安裝:
npm install sinon -D
配置完成后,需要在使用的地方進(jìn)行引入,如下所示:
const sinon = require("sinon");
在我的項(xiàng)目中,主要是使用Sinon.js來(lái)模擬HTTP請(qǐng)求。在Sinon.js的文檔中,有專門關(guān)于XMLHttpRequest對(duì)象的模擬的章節(jié),在下一章中,我們將會(huì)針對(duì)項(xiàng)目中sinon.js的使用進(jìn)行簡(jiǎn)單的介紹。
編寫單元測(cè)試在本章中,我們會(huì)針對(duì)如何編寫單元測(cè)試文件進(jìn)行一個(gè)具體的講解,其中包含:
同步函數(shù)測(cè)試
異步函數(shù)測(cè)試
HTTP測(cè)試
同時(shí),我們會(huì)對(duì)當(dāng)中使用到的Jest和Sinon.js的API會(huì)進(jìn)行簡(jiǎn)單介紹,如果需要使用其他的API,可以自行閱讀Jest和Sinon.js的文檔。
通過(guò)上面三類測(cè)試,我們基本能夠覆蓋現(xiàn)有項(xiàng)目中的所有代碼。
同步函數(shù)測(cè)試同步函數(shù)的測(cè)試過(guò)程是這幾個(gè)中最簡(jiǎn)單的一部分,我們可以測(cè)試函數(shù)返回值,也能夠測(cè)試傳入的高階函數(shù)。下面我們通過(guò)一個(gè)具體的例子來(lái)看下。
源代碼文件,一個(gè)純函數(shù):
// user.js export default function(obj) { return "hjava"; } export function handleUserData(callback) { callback("hjava"); }
針對(duì)上面的源代碼文件編寫的一個(gè)單元測(cè)試文件:
// user.test.js import userFunc, {handleUserData} from "./user"; // test是一個(gè)注冊(cè)的全局方法 test("user", () => { expect(userFunc()).toBe("hjava"); // 判斷userFunc的執(zhí)行結(jié)果等于"hjava" let callback = jest.fn(); // jest是一個(gè)注冊(cè)的全局變量 handleUserData(callback); expect(callback.mock.calls.length).toBe(1); // 判斷callback函數(shù)被調(diào)用了一次 expect(callback.mock.calls[0][0]).toBe("hjava"); // 判斷了callback函數(shù)的第一次被調(diào)用的第一個(gè)參數(shù)為"hjava" });
從上面的示例中我們可以看到,針對(duì)同步的純函數(shù),我們可以通過(guò)很簡(jiǎn)單的單元測(cè)試模型來(lái)驗(yàn)證它的功能。
異步函數(shù)測(cè)試異步函數(shù)主要分為兩種——Callback方式和Promise方式。這兩種方式都很簡(jiǎn)單,下面我們對(duì)兩種方式進(jìn)行具體的介紹。詳細(xì)內(nèi)容可以見Jest文檔中的測(cè)試異步代碼。
Callback方式// user.js export default function(callback) { setTimeout(()=>{ callback({username: "hjava"}); }, 1000); }
// user.test.js import userFunc from "./user"; test("user", () => { userFunc((data) => { expect(data).toEqual({username: "hjava"}); // 對(duì)象比較用beEqual() }); });Promise方式
// user.js export default function(callback) { return Promise.resolve({username: "hjava"}); }
// user.test.js import userFunc from "./user"; test("user", () => { userFunc().then((data) => { expect(data).toEqual({username: "hjava"}); }); });HTTP測(cè)試
在測(cè)試HTTP請(qǐng)求相關(guān)參數(shù)的過(guò)程中,我們需要模擬XMLHttpRequest對(duì)象,從而攔截相關(guān)的HTTP請(qǐng)求,獲取請(qǐng)求數(shù)據(jù)。正好Sinon.js能夠做到這一點(diǎn)。下面我們通過(guò)一個(gè)示例來(lái)看下相關(guān)的邏輯:
// user.js export default function(callback) { this.sendRequest("/user/get", callback); // 發(fā)送請(qǐng)求來(lái)獲取用戶數(shù)據(jù),成功后執(zhí)行callback回調(diào)函數(shù) }
// user.test.js import Sinon from "sinon"; import userFunc from "user"; let XHR; let requests = []; // beforeEach是Jest提供的函數(shù),在每個(gè)測(cè)試執(zhí)行前都會(huì)執(zhí)行一次 beforeEach(() => { XHR = sinon.useFakeXMLHttpRequest(); //創(chuàng)建一個(gè)模擬的XMLHttpRequest對(duì)象 XHR.onCreate = function (xhr) { requests.push(xhr); }; }); // afterEach是Jest提供的函數(shù),在每個(gè)測(cè)試執(zhí)行后都會(huì)執(zhí)行一次 afterEach(() => { XHR.restore(); }); test("user", () => { let callback = jest.fn(); HTTPCommon.deleteRemoteSession({ data: {}, success: callback }); expect(requests.length).toBe(1); requests[0].respond(200, {"Content-Type": "application/json"}, "hjava"); // 模擬返回值 expect(callback.mock.calls[0][0]).toBe("hjava"); });如何解決進(jìn)行單元測(cè)試中遇到的常見問(wèn)題?
在本章中,我們總結(jié)了如下問(wèn)題來(lái)進(jìn)行介紹,希望大家再遇到相同問(wèn)題時(shí)能夠快速解決:
如何統(tǒng)計(jì)Jest單元測(cè)試覆蓋率
如何設(shè)置單元測(cè)試文件不使用本地的babel配置
如何設(shè)置單元測(cè)試文件使用本地的babel配置
如何處理代碼中引用的webpack alias問(wèn)題
如何統(tǒng)計(jì)單元測(cè)試覆蓋率?不像ava一樣,需要使用syc來(lái)進(jìn)行計(jì)算,Jest內(nèi)置了統(tǒng)計(jì)單元測(cè)試覆蓋率的工具,只需要簡(jiǎn)單配置即可達(dá)到相關(guān)的要求。具體配置如下:
// package.json { "jest": { "collectCoverage": true, // 是否開啟統(tǒng)計(jì)單元測(cè)試覆蓋率 "collectCoverageFrom": [ // 指定統(tǒng)計(jì)單元測(cè)試覆蓋率文件 "**/src/**.js" ], } }如何設(shè)置單元測(cè)試文件不使用ES2015配置
如果你的項(xiàng)目中有.babelrc文件,而你不希望單元測(cè)試文件受到babel文件的影響,你可以在jest的配置項(xiàng)中增加transform字段,具體配置如下:
// package.json { "jest": { "transform": {} } }如何設(shè)置單元測(cè)試使用ES2015配置
如果你的單元測(cè)試文件中需要使用ES2015后通過(guò)babel來(lái)進(jìn)行編譯,那么需要對(duì).babelrc文件的配置進(jìn)行部分修改。
如果你之前在.babelrc文件中,把modules字段設(shè)置為false,那么你需要在test環(huán)境下重新開啟,具體代碼如下:
// .babelrc { "presets": [["env", {"modules": false}]], "env": { "test": { "presets": [["env"]] } } }
如果你使用的是babel 7的話(安裝時(shí)多安裝過(guò)相關(guān)依賴包),你需要設(shè)置的presets字段的值應(yīng)該為@babel/env,具體代碼如下:
// .babelrc { "presets": [["env", {"modules": false}]], "env": { "test": { "presets": [["@babel/env"]] } } }如何處理代碼中引用的webpack alias問(wèn)題
如果我們?cè)陧?xiàng)目中使用了webpack,那么我們很大概率會(huì)使用到alias相關(guān)屬性來(lái)定義路徑。但是,在單元測(cè)試框架中,它并不能夠識(shí)別這種路徑,就會(huì)出現(xiàn)Cannot find module "xxx" from "yyy"的報(bào)錯(cuò)。
不像ava框架需要安裝插件和進(jìn)行復(fù)雜的配置,我們只需要在Jest中配置moduleNameMapper屬性即可滿足需求。具體示例如下:
// webpack.config.js { alias: { "@__dir":process.cwd() } }
//package.json { "jest": { "moduleNameMapper": { "@__dir(.*)$": "總結(jié)$1" //正則匹配方式,對(duì)應(yīng)webpack alias } } }
編寫測(cè)試是一個(gè)很好的習(xí)慣。
很多人經(jīng)常都說(shuō)要對(duì)自己的代碼進(jìn)行質(zhì)量監(jiān)控,但是又不知道該如何下手。通過(guò)這篇文章,你應(yīng)該學(xué)會(huì)了如何針對(duì)已有代碼從零開始編寫一套完整的單元測(cè)試用例。
如果有任何疑問(wèn),歡迎留言或者私信進(jìn)行溝通與交流。
關(guān)于Jest是如何測(cè)試JavaScript代碼以及Sinon是如何模擬XMLHttpRequest請(qǐng)求的,我們將會(huì)在后面幾篇博客中給大家?guī)?lái)相關(guān)的源碼解析,有興趣的同學(xué)可以關(guān)注我,留意后續(xù)的文章。
附錄Jest
Sinon.js
ava
ava關(guān)于配置解決webpack alias的issue
Mocha
Chai
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/94392.html
摘要:概述在我們進(jìn)行單元測(cè)試的過(guò)程中,如果我們需要對(duì)一些接口進(jìn)行相關(guān)的業(yè)務(wù)測(cè)試,那么我們就需要來(lái)模擬請(qǐng)求的發(fā)送與響應(yīng),否則我們就無(wú)法完成測(cè)試的閉環(huán)。我們?cè)偻ㄟ^(guò)記錄的數(shù)據(jù),組合其他的單元測(cè)試框架來(lái)對(duì)業(yè)務(wù)代碼進(jìn)行測(cè)試。 概述 在我們進(jìn)行單元測(cè)試的過(guò)程中,如果我們需要對(duì)一些HTTP接口進(jìn)行相關(guān)的業(yè)務(wù)測(cè)試,那么我們就需要來(lái)模擬HTTP請(qǐng)求的發(fā)送與響應(yīng),否則我們就無(wú)法完成測(cè)試的閉環(huán)。 目前,有許許多多...
摘要:的配置文件是為了解析那些需要測(cè)試的源文件相關(guān)的文件,然后再給的單元測(cè)試用例去識(shí)別。其作用是僅僅渲染至虛擬節(jié)點(diǎn),不會(huì)返回真實(shí)的節(jié)點(diǎn),能極大提高測(cè)試性能。 為解放勞動(dòng)力,發(fā)展生產(chǎn)力 測(cè)試有了這般變化: 鼠標(biāo)點(diǎn)擊手動(dòng)測(cè)試 -> 用腳本模擬,自動(dòng)化測(cè)試 Vue中的組件測(cè)試 需要安裝的包 全局安裝:babel、mocha、karma 其他局部安裝的包在下面的【測(cè)試環(huán)境搭建】最下方配置文件中給出...
摘要:隨著應(yīng)用的復(fù)雜程度越來(lái)越高,很多公司越來(lái)越重視前端單元測(cè)試。最后我們可以利用覆蓋率來(lái)看下用例的覆蓋程度是否足夠一般來(lái)說(shuō)不用刻意追求,根據(jù)實(shí)際情況來(lái)定單元測(cè)試是測(cè)試驅(qū)動(dòng)開發(fā)的基礎(chǔ)。 隨著 Web 應(yīng)用的復(fù)雜程度越來(lái)越高,很多公司越來(lái)越重視前端單元測(cè)試。我們看到的大多數(shù)教程都會(huì)講單元測(cè)試的重要性、一些有代表性的測(cè)試框架 api 怎么使用,但在實(shí)際項(xiàng)目中單元測(cè)試要怎么下手?測(cè)試用例應(yīng)該包含哪...
摘要:加上測(cè)試覆蓋率檢查,就能夠提供足夠的信息,來(lái)斷言代碼的行為是否符合期望。測(cè)試的相關(guān)技術(shù)是程序的代碼覆蓋率工具,以土耳其最大城市伊斯坦布爾命名。 showImg(https://segmentfault.com/img/remote/1460000010260434); 敏捷軟件開發(fā)中,最重要實(shí)踐的就是測(cè)試驅(qū)動(dòng)開發(fā),在單元測(cè)試層面,我們?cè)囍鴮?shí)現(xiàn)一個(gè)重要的指標(biāo)就是測(cè)試覆蓋率。測(cè)試覆蓋率衡量...
摘要:為什么要寫單元測(cè)試減少提高代碼質(zhì)量,保證你的代碼是可測(cè)試的放心重構(gòu)當(dāng)你每個(gè)方法都寫了單元測(cè)試的時(shí)候,你每一個(gè)改動(dòng)都會(huì)影響相應(yīng)的單元測(cè)試,這樣你不用費(fèi)盡心思的考慮哪里會(huì)有影響,特別是復(fù)雜項(xiàng)目或非核心功能不易被測(cè)試到,從而導(dǎo)致的產(chǎn)生。 為什么要寫單元測(cè)試 減少bug 提高代碼質(zhì)量,保證你的代碼是可測(cè)試的 放心重構(gòu) 當(dāng)你每個(gè)方法都寫了單元測(cè)試的時(shí)候,你每一個(gè)改動(dòng)都會(huì)影響相應(yīng)的單元測(cè)試,這...
閱讀 4716·2021-09-09 09:33
閱讀 2441·2019-08-29 17:15
閱讀 2430·2019-08-29 16:21
閱讀 1040·2019-08-29 15:06
閱讀 2682·2019-08-29 13:25
閱讀 636·2019-08-29 11:32
閱讀 3306·2019-08-26 11:55
閱讀 2649·2019-08-23 18:24