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