React開發(fā)中要進行單測,主要是針對UI層的 React Components 和業(yè)務(wù)流程的 Redux Saga來進行。
Jest 簡介
Jest 是由facebook提供的一個開源 Javascript 測試框架,由于和 React 同出一源,所以能很好的和 React 程序結(jié)合使用。
- Jest 是基于Jasmine 2實現(xiàn)的,所以沿用了Jasmine的Matcher,熟悉Jasmine的可以直接上手。
- Jest 執(zhí)行環(huán)境默認是使用的Node.js 上的 jsdom,所以對于 DOM 的操作不依賴瀏覽器,通過命令行完成,因此不需要安裝任何別的測試運行環(huán)境。
- 支持 Mock,覆蓋率,Snapshots等測試工具。
- 優(yōu)先執(zhí)行前回出錯的測試,支持并行執(zhí)行,執(zhí)行速度很快。
Enzyme 簡介
Enzyme 是基于React原生Test Utilities的一個擴展工具包,能夠更好的幫助完成React的控件測試。提供了一套類 Jquery查詢器語法的選擇方法,對于定位Dom元素相當方便。Enzyme 也是 React 官網(wǎng)推薦的進行單測的工具,可以認為是標配了。Test Utilities
開始實現(xiàn)
安裝必要包和類型定義文件
yarn add -D jest @types/jest enzyme enzyme-adapter-react-16 @types/enzyme @types/enzyme-adapter-react-16
這里的 enzyme-adapter-react-16 包是為了建立 React 和 enzyme的一個適配器。
接著我們在根目錄創(chuàng)建 jest.config.js 的 Jest 配置文件。
module.exports = {
// 設(shè)置jsdom運行環(huán)境的URL
// 默認值:: http://localhost
testURL: 'http://localhost/',
// glob模式指定查找測試文件的規(guī)則
// 默認值:: [ "**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)" ]
testMatch: [
'<rootDir>/**/__tests__/**/*.{js,jsx,ts,tsx}',
'<rootDir>/**/*.{spec,test}.{js,jsx,ts,tsx}',
],
// 項目模塊中使用的文件擴展名數(shù)組
// 默認值:: ["js", "json", "jsx", "ts", "tsx", "node"]
// 因為是從左到右尋找,推薦將自項目常用的文件擴展名排在前面
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
// 每個測試文件運行前執(zhí)行指定代碼配置或者是創(chuàng)建測試環(huán)境
// 默認值:[]
setupFiles: [
'<rootDir>/__test__/test.setup.ts',
],
// 指定測試環(huán)境
// 默認值︰"jsdom",這里是指jsdom11
// 可以安裝自定義的測試環(huán)境,來指定對應(yīng)的jsdom版本,例如jest-environment-jsdom-fourteen
testEnvironment: 'jsdom',
// 指定模塊的搜索路徑
// 默認值:[]
modulePaths: ['<rootDir>/src/'],
// 允許使用一個簡單的module,來替換類似圖片,樣式類的資源
// 默認值︰null
moduleNameMapper: {}
};
Jest 的配置相當豐富,你可以通過一個簡單的配置擴展自己的實際項目需求,具體配置請參照官方文檔 Configuring Jest
完成配置后,在根目錄新建如下項目結(jié)構(gòu)
__test__
├─ components/
└─ sagas/
test.setup.ts
如上面配置所示,test.setup.ts完成了再運行測試之前,對于 Enzyme 的配置啟動。
test.setup.ts
/* eslint-disable import/no-unresolved */
import Enzyme from "enzyme";
import Adapter from "enzyme-adapter-react-16";
Enzyme.configure({ adapter: new Adapter() });
我們分別針對組件 Header 和 novel的saga業(yè)務(wù)流程增加單元測試。
Header.spec.tsx
/* eslint-disable import/no-unresolved */
import React from "react";
import { shallow } from "enzyme";
import Header from "../../src/components/Header";
describe("<Header />", () => {
it("renders four li tag", () => {
const wrapper = shallow(<Header />);
expect(wrapper.find("ul Link")).toHaveLength(4);
});
});
novel.spec.ts
import { watchSearchNovels } from "../../src/sagas/novel";
import { take, call } from "redux-saga/effects";
import { fetchNovels } from "../../src/actions/novel";
import { queryNovels } from "../../src/services/novelapi";
test("watchSearchNovels Saga test", () => {
const gen = watchSearchNovels();
expect(gen.next().value).toEqual(take(fetchNovels));
expect(gen.next().value).toEqual(call(queryNovels));
});
以上演示的只是簡單應(yīng)用,對于復(fù)雜的測試可能需要增加Mock操作或者引入其他工具插件,后續(xù)會陸續(xù)記錄追加。
最后我們修改package.json,增加對應(yīng)的執(zhí)行命令。
"scripts": {
"build": "webpack --config webpack.prod.js",
"build_dev": "webpack --config webpack.dev.js",
"dev": "webpack-dev-server --open --config webpack.dev.js",
"mock": "node ./src/mock/mock-server.js",
"test": "jest",
"test_dev": "jest --coverage"
},
執(zhí)行yarn test
如此我們可以愉快的開始寫單體測試代碼了。