- 蘇格團隊
- 做者:Dee
單元測試的好處:css
因爲筆者開發的項目愈來愈大,公共組件的複用性高,故其穩定性尤其重要。所以,引入單元測試刻不容緩。html
單元測試的很差:node
在作項目單元測試前,筆者參考了網上的一些文章以及官方文檔,最後選型爲Jest + react-test-renderer + Enzyme。react
Jestgit
Jest 是 Facebook 出品的一個測試框架,相對其餘測試框架,其一大特色就是就是集成了 Mocha,chai,jsdom,sinon等功能,內置了經常使用的測試工具,好比自帶斷言、測試覆蓋率工具,實現了開箱即用。github
react-test-renderweb
配合react-test-render,Jest 可提供了快照測試功能。npm
首次運行快照測試,會產生一個可讀的快照,再次測試時會經過比對快照文件和新產生的快照判斷測試是否經過。json
Jest在執行的時候若是發現toMatchSnapshot方法,會在同級目錄下生成一個__ snapshots__文件夾用來存放快照文件,之後每次測試的時候都會和第一次生成的快照進行比較。瀏覽器
Enzyme
React官方已經提供了一個測試工具庫:react-dom/test-utils。可是用起來不夠方便,因而有了一些第三方的封裝庫,好比Airbnb公司的Enzyme。其兩大特色:
三種渲染方法
shallow:淺渲染,是對官方的Shallow Renderer的封裝。將組件渲染成虛擬DOM對象,只會渲染第一層,子組件將不會被渲染出來,使得效率很是高。不須要DOM環境, 並可使用jQuery的方式訪問組件的信息
render:靜態渲染,它將React組件渲染成靜態的HTML字符串,而後使用Cheerio這個庫解析這段字符串,並返回一個Cheerio的實例對象,能夠用來分析組件的html結構
mount:徹底渲染,它將組件渲染加載成一個真實的DOM節點,用來測試DOM API的交互和組件的生命週期。用到了jsdom來模擬瀏覽器環境
三種方法中,shallow和mount由於返回的是DOM對象,能夠用simulate進行交互模擬,而render方法不能夠。通常shallow方法就能夠知足需求,若是須要對子組件進行判斷,須要使用render,若是須要測試組件的生命週期,須要使用mount方法。
注意:enzyme還須要根據React的版本安裝適配器,適配器對應表以下:
前面說了這麼多,是時候上代碼了。
目錄
筆者在根目錄新建一個unitTest目錄,其目錄結構爲:
jest.config.js:jest配置文件
mocks:mock文件目錄
components:項目的公共組件單元測試用例目錄
components/__ snapshots __:運行單元測試時自動生成的快照存放目錄
安裝(因爲筆者是react16版本,因此安裝的適配器版本爲enzyme-adapter-react-16)
npm install jest enzyme enzyme-adapter-react-16 react-test-renderer
配置
Jest支持直接在package.json文件寫入配置,但筆者有輕微潔癖,喜歡把配置文件寫到unitTest裏面,方便查找以及閱讀。
// package.json
{
"scripts": {
"jest": "jest --config ./unitTest/jest.config.js", // 單元測試
"jestupdate": "jest --config ./unitTest/jest.config.js --updateSnapshot" // 單元測試快照更新
"jestreport": "jest --config ./unitTest/jest.config.js --coverage" // 單元測試並生成覆蓋率報告
}
}
複製代碼
// jest.config.js
module.exports = {
testURL: 'http://localhost/',
setupFiles: [],
moduleFileExtensions: ['js', 'jsx'],
testPathIgnorePatterns: ['/node_modules/'],
testRegex: '.*\\.test\\.js$',
collectCoverage: false,
collectCoverageFrom: ['src/components/**/*.{js}'],
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/mocks/fileMock.js',
'\\.(css|less|scss)$': '<rootDir>/mocks/styleMock.js'
}
};
複製代碼
mock文件
// fileMock.js
module.exports = {};
複製代碼
// styleMock.js
module.exports = {};
複製代碼
編寫單元測試
// button.test.js
import Button from '../../src/common/components/Button';
import renderer from 'react-test-renderer';
import React from 'react';
import { shallow, configure } from 'enzyme'; // shallow(淺渲染,只渲染父組件)
import Adapter from 'enzyme-adapter-react-16'; // 適應React-16
configure({ adapter: new Adapter() }); // 適應React-16,初始化
const props = {
text: '按鈕測試用例',
type: 'white',
style: { marginTop: 15 },
size: 'big',
disabled: false,
height: 'middle',
isLock: true,
cname: 'hello',
onClick: () => {}
};
describe('test Button', () => {
it('button render correctly', () => {
const tree = renderer.create(<Button {...props} />).toJSON();// 生成快照
expect(tree).toMatchSnapshot(); // 匹配以前的快照
});
it('button has class', () => {
const item = shallow(<Button {...props} />); //淺渲染
expect(item.hasClass('hello')).toBe(true); // 斷言有item有hello的className
});
});
複製代碼
注意事項:
一、若是不配置testURL,會報錯:localStorage is not available for opaque origins
二、本文檔只講述筆者的實踐方案以供參考,關於Jest、enzyme的具體介紹、用法可參考