使用 Jest 和 Enzyme 测试 React 组件
- type: FrontEnd
- title: Testing React components with Jest and Enzyme
- link: hackernoon.com/testing-rea…
- author: Artem Sapegin
引言
有人說在通常情況下測試 React 組件是沒有太大用處的,但是我覺著在一些場景下是很有必要的:
- 組件庫,
- 開源項目,
- 與第三方組件集成,
- bugs, 防止復現.
我嘗試過很多的工具組合,但是最終如果會推薦給別的開發者,我更樂意去擇推薦如下組合:
- Jest, 一個測試框架;
- Enzyme, React的 測試類庫;
- enzyme-to-json 轉換 Enzyme 包裝類型匹配 Jest 的快照
我經常在測試中使用的是淺渲染和 Jest 快照測試。
在 Jest 進行快照測試
Shallow rendering
淺渲染指的是將一個組件渲染成虛擬 DOM 對象,但是只渲染第一層,不渲染所有子組件。所以即使你對子組件做了一下改動卻不會影響淺渲染的輸出結果。或者是引入的子組件中發生了 bug,也不會對父組件的淺渲染結果產生影響。淺渲染是不依賴 DOM 環境的。
舉個例子:
const ButtonWithIcon = ({icon, children}) => (<button><Icon icon={icon} />{children}</button> ); 復制代碼在 React 中將會被渲染成如下:
<button><i class="icon icon_coffee"></i>Hello Jest! </button> 復制代碼但是在淺渲染中只會被渲染成如下結果:
<button><Icon icon="coffee" />Hello Jest! </button> 復制代碼需要注意的是 Icon 組件并未被渲染出來。
快照測試
Jest 快照就像那些帶有由文本字符組合而成表達窗口和按鈕的靜態UI:它是存儲在文本文件中的組件的渲染輸出。
你可以告訴 Jest 哪些組件輸出的 UI 不會有意外的改變,那么 Jest 在運行時會將其保存到如下所示的文件中:
exports[`test should render a label 1`] = ` <labelclassName="isBlock">Hello Jest! </label> `;exports[`test should render a small label 1`] = ` <labelclassName="isBlock isSmall">Hello Jest! </label> `; 復制代碼每次更改組件時,Jest 都會與當前測試的值進行比較并顯示差異,并且會在你做出修改是要求你更新快照。
除了測試之外,Jest 將快照存儲在類似 __snapshots __ / Label.spec.js.snap 這樣的文件中,同時你需要提交這些文件。
為什么選擇 Jest
- 運行速度非常快。
- 可以進行快照測試。
- 交互式的監控模式,只會測試有過修改的部分。
- 錯誤信息很詳細。
- 配置簡單。
- Mocks 和 spies 支持.
- 通過命令行可以生成測試報告.
- 發展前景很好。
- 不會寫出像 Chai 框架 'expect(foo).to.be.a(‘function’)' 一樣很容易出錯的斷言 'expect(foo).to.be.a.function' 因為 Jest 只會寫 'expect(foo).to.be.true' 這樣確定正確的斷言。
為什么選擇 Enzyme
- 便利的工具函數庫封裝,可以處理淺渲染,靜態渲染標記以及DOM渲染。
- jQuery 風格的API,便于使用和直觀。
配置
第一步安裝所有的依賴包括同版本依賴:
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(); }); 復制代碼Props 測試
有的時候如果你想測試的更精確和看到真實的值。那樣的話需要在 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); }); 復制代碼不僅僅是JSX
Jest使用JSON進行快照測試,因此你可以測試返回JSON的任何函數,方法與測試組件相同:
test('accept custom properties', () => {const wrapper = shallow(<LayoutflexBasis={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:42Warning: 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'; 復制代碼參考來源
- Jest cheat sheet
- Testing React Applications by Max Stoiber
- Migrating to Jest by Kent C. Dodds
- Migrating Ava to Jest by Jason Brown
轉載于:https://juejin.im/post/5cbd5cc4e51d456e79545c82
總結
以上是生活随笔為你收集整理的使用 Jest 和 Enzyme 测试 React 组件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我在大学毕业后学习Linux、pytho
- 下一篇: Sql去重一些技巧