概述
在一樣平常的功用開闢中,我們的代碼測試都依靠於本身或許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 -D
package.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(.*)$": "<rootDir>$1" //正則婚配體式格局,對應webpack alias
}
}
}
總結
編寫測試是一個很好的習氣。
很多人常常都說要對本身的代碼舉行質量監控,然則又不知道該怎樣動手。經由歷程這篇文章,你應當學會了怎樣針對已有代碼從零最先編寫一套完全的單元測試用例。
假如有任何疑問,迎接留言或許私信舉行溝通與交換。
關於Jest是怎樣測試JavaScript代碼以及Sinon是怎樣模仿XMLHttpRequest請求的,我們將會在後面幾篇博客中給人人帶來相干的源碼剖析,有興緻的同硯可以關注我,注意後續的文章。