有人說在一般狀況下測試 React 組件是沒有太大用處的,可是我覺着在一些場景下是頗有必要的:javascript
我嘗試過不少的工具組合,可是最終若是會推薦給別的開發者,我更樂意去擇推薦以下組合:css
我常常在測試中使用的是淺渲染和 Jest 快照測試。html
在 Jest 進行快照測試java
淺渲染指的是將一個組件渲染成虛擬 DOM 對象,可是隻渲染第一層,不渲染全部子組件。因此即便你對子組件作了一下改動卻不會影響淺渲染的輸出結果。或者是引入的子組件中發生了 bug,也不會對父組件的淺渲染結果產生影響。淺渲染是不依賴 DOM 環境的。node
舉個例子:react
const ButtonWithIcon = ({icon, children}) => (
<button><Icon icon={icon} />{children}</button> ); 複製代碼
在 React 中將會被渲染成以下:git
<button>
<i class="icon icon_coffee"></i>
Hello Jest!
</button>
複製代碼
可是在淺渲染中只會被渲染成以下結果:github
<button>
<Icon icon="coffee" /> Hello Jest! </button>
複製代碼
須要注意的是 Icon 組件並未被渲染出來。npm
Jest 快照就像那些帶有由文本字符組合而成表達窗口和按鈕的靜態UI:它是存儲在文本文件中的組件的渲染輸出。json
你能夠告訴 Jest 哪些組件輸出的 UI 不會有意外的改變,那麼 Jest 在運行時會將其保存到以下所示的文件中:
exports[`test should render a label 1`] = ` <label className="isBlock"> Hello Jest! </label> `;
exports[`test should render a small label 1`] = ` <label className="isBlock isSmall"> Hello Jest! </label> `;
複製代碼
每次更改組件時,Jest 都會與當前測試的值進行比較並顯示差別,而且會在你作出修改是要求你更新快照。
除了測試以外,Jest 將快照存儲在相似 __snapshots __ / Label.spec.js.snap
這樣的文件中,同時你須要提交這些文件。
第一步安裝全部的依賴包括同版本依賴:
npm install --save-dev jest react-test-renderer enzyme enzyme-adapter-react-16 enzyme-to-json
複製代碼
還須要安裝 Babel 插件 babel-jest 或者 TypeScript 插件 ts-jest
更新工程的 package.json 文件:
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
},
"jest": {
"setupFiles": ["./test/jestsetup.js"],
"snapshotSerializers": ["enzyme-to-json/serializer"]
}
複製代碼
配置項 'snapshotSerializers' 容許你經過配置 'enzyme-to-json',把 Enzyme 的封裝類型傳給 'Jest' 的快照匹配項中,從而不須要手動進行轉化。
建立一個 test/jestsetup.js 的文件來自定義 Jest 的運行環境(上面的 setupFiles 配置項)
import Enzyme, { shallow, render, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
// React 16 Enzyme adapter
Enzyme.configure({ adapter: new Adapter() });
// Make Enzyme functions available in all test files without importing
global.shallow = shallow;
global.render = render;
global.mount = mount;
複製代碼
針對 css 模塊也能夠添加下面的配置到package.json
"jest": {
"moduleNameMapper": {
"^.+\\.(css|scss)$": "identity-obj-proxy"
}
}
複製代碼
And run:
同時安裝依賴:
npm install --save-dev identity-obj-proxy
複製代碼
注意 identity-obj-proxy 依賴的 node 版本是 Node 4或者 Node 5須要開啓 'harmony-proxies'
對於大部分沒有交互的組件,下面的測試用例已經足夠:
test('render a label', () => {
const wrapper = shallow(
<Label>Hello Jest!</Label>
);
expect(wrapper).toMatchSnapshot();
});
test('render a small label', () => {
const wrapper = shallow(
<Label small>Hello Jest!</Label>
);
expect(wrapper).toMatchSnapshot();
});
test('render a grayish label', () => {
const wrapper = shallow(
<Label light>Hello Jest!</Label>
);
expect(wrapper).toMatchSnapshot();
});
複製代碼
有的時候若是你想測試的更精確和看到真實的值。那樣的話須要在 Enzyme API 中使用 Jest的 斷言。
test('render a document title', () => {
const wrapper = shallow(
<DocumentTitle title="Events" />
);
expect(wrapper.prop('title')).toEqual('Events');
});
test('render a document title and a parent title', () => {
const wrapper = shallow(
<DocumentTitle title="Events" parent="Event Radar" />
);
expect(wrapper.prop('title')).toEqual('Events — Event Radar');
});
複製代碼
有的時候你不能用快照。好比組件裏面有隨機ID像下面的代碼:
test('render a popover with a random ID', () => {
const wrapper = shallow(
<Popover>Hello Jest!</Popover>
);
expect(wrapper.prop('id')).toMatch(/Popover\d+/);
});
複製代碼
你能夠模擬相似 'click' 或者 'change'這樣的事件而後把組件和快照作比較:
test('render Markdown in preview mode', () => {
const wrapper = shallow(
<MarkdownEditor value="*Hello* Jest!" /> ); expect(wrapper).toMatchSnapshot(); wrapper.find('[name="toggle-preview"]').simulate('click'); expect(wrapper).toMatchSnapshot(); }); 複製代碼
有的時候你想要測試一個子組件中一個元素是怎樣影響組件的。你須要使用 Enzyme的 mount 方法來渲染一個真實的 DOM。
test('open a code editor', () => {
const wrapper = mount(
<Playground code={code} /> ); expect(wrapper.find('.ReactCodeMirror')).toHaveLength(0); wrapper.find('button').simulate('click'); expect(wrapper.find('.ReactCodeMirror')).toHaveLength(1); }); 複製代碼
相似於在事件測試中,由使用快照測試組件的輸出呈現替換爲使用Jest的mock函數來測試事件處理程序自己:
test('pass a selected value to the onChange handler', () => {
const value = '2';
const onChange = jest.fn();
const wrapper = shallow(
<Select items={ITEMS} onChange={onChange} /> ); expect(wrapper).toMatchSnapshot(); wrapper.find('select').simulate('change', { target: { value }, }); expect(onChange).toBeCalledWith(value); }); 複製代碼
Jest使用JSON進行快照測試,所以你能夠測試返回JSON的任何函數,方法與測試組件相同:
test('accept custom properties', () => {
const wrapper = shallow(
<Layout flexBasis={0} flexGrow={1} flexShrink={1} flexWrap="wrap" justifyContent="flex-end" alignContent="center" alignItems="center" /> ); expect(wrapper.prop('style')).toMatchSnapshot(); }); 複製代碼
調試淺層渲染器輸出
Use Enzyme’s debug method to print shallow renderer’s output: 使用Enzyme的調試方法打印千層渲染器的輸出:
const wrapper = shallow(/*~*/);
console.log(wrapper.debug());
複製代碼
啓用覆蓋範圍的失敗測試
當你的測試失敗時,帶有覆蓋範圍標誌的diff以下所示:
-<Button
+<Component
複製代碼
嘗試將箭頭函數組件替換爲常規函數組建:
- export default const Button = ({ children }) => {
+ export default function Button({ children }) {
複製代碼
requestAnimationFrame 錯誤
當你運行你的測試時,你可能會看到以下錯誤:
console.error node_modules/fbjs/lib/warning.js:42
Warning: React depends on requestAnimationFrame. Make sure that you load a polyfill in older browsers. http://fb.me/react-polyfills
複製代碼
React 16依賴於requestAnimationFrame
,所以你須要在你的測試代碼中添加一個polyfill
// test/jestsetup.js
import 'raf/polyfill';
複製代碼