若是你想學習 React 單元測試,那就從這篇文章開始吧。Star 項目,clone 到本地,根據教程走一遍,有任何問題歡迎 issue 討論。javascript
項目GitHub地址: react-test-demo
文章主要內容以下:css
測試腳本編寫html
Jest 是 Facebook 發佈的一個開源的、基於 Jasmine
框架的 JavaScript 單元測試工具。提供了包括內置的測試環境 DOM API 支持、斷言庫、Mock 庫等,還包含了 Spapshot Testing、 Instant Feedback 等特性。前端
Airbnb開源的 React 測試類庫 Enzyme 提供了一套簡潔強大的 API,並經過 jQuery 風格的方式進行DOM 處理,開發體驗十分友好。不只在開源社區有超高人氣,同時也得到了React 官方的推薦。java
在開發 React 應用的基礎上(默認你用的是 Webpack + Babel 來打包構建應用),你須要安裝 Jest
Enzyme
,以及對應的 babel-jest
node
npm install jest enzyme babel-jest --save-dev
下載 npm 依賴包以後,你須要在 package.json
中新增屬性,配置 Jest:react
"jest": { "moduleFileExtensions": [ "js", "jsx" ], "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" }, "transform": { "^.+\\.js$": "babel-jest" } },
並新增test scripts
webpack
"scripts": { "dev": "NODE_ENV=development webpack-dev-server --inline --progress --colors --port 3000 --host 0.0.0.0 ", "test": "jest" }
其中 :git
moduleFileExtensions
:表明支持加載的文件名,與 Webpack 中的 resolve.extensions
相似moduleNameMapper
:表明須要被 Mock 的資源名稱。若是須要 Mock 靜態資源(如less、scss等),則須要配置 Mock 的路徑 <rootDir>/__mocks__/yourMock.js
transform
用於編譯 ES6/ES7 語法,需配合 babel-jest
使用上面三個是經常使用的配置,更多 Jest 配置見官方文檔:Jest Configurationgithub
環境搭建好了,就能夠開始動手寫測試腳本了。在開始以前,先分析下 Todo 應用的組成部分。
應用主體結構以下 src/component/App.js
:
class App extends Component { render() { const { params } = this.props; return ( <section className="todoapp"> <div className="main"> <AddTodo /> <VisibleTodoList filter={params.filter || 'all'} /> </div> <Footer /> </section> ) } }
能夠發現 整個應用能夠分爲三個組件:
<App />
<AddTodo />
<VisibleTodoList />
其中 <App/>
是 UI 組件,<AddTodo />
和 <VisibleTodoList />
是智能組件,咱們須要找到智能組件所對應的 UI 組件 <AddTodoView/>
和 <TodoList/>
。
<AddTodoView/>
就是一個 Input
輸入框,接受文字輸入,敲下回車鍵,建立一個 Todo。代碼以下 src/component/AddTodoView.js
:
import React, { Component, PropTypes } from 'react' class AddTodoView extends Component { render() { return ( <header className="header"> <h1>todos</h1> <input className="new-todo" type="text" onKeyUp={e => this.handleClick(e)} placeholder="input todo item" ref='input' /> </header> ) } handleClick(e) { if (e.keyCode === 13) { const node = this.refs.input; const text = node.value.trim(); text && this.props.onAddClick(text); node.value = ''; } } }
瞭解了該組件的功能以後,咱們首先須要明確該組件須要測試哪些點:
props
傳遞的 onAddClick(text)
方法props
傳遞的 onAddClick(text)
方法通過上面的分析以後,咱們就能夠開始編寫單元測試腳本了。
import React from 'react' import App from '../../src/component/App' import { shallow } from 'enzyme'
在這裏咱們引入了 shallow
方法,它是 Enzyme
提供的 API 之一,能夠實現淺渲染。其做用是僅僅渲染至虛擬節點,不會返回真實的節點,能極大提升測試性能。可是它不適合測試包含子組件、須要測試聲明週期的組件。Enzyme
還提供了其餘兩個 API:
mount
:Full Rendering,很是適用於存在於 DOM API 存在交互組件,或者須要測試組件完整的聲明週期render
:Static Rendering,用於 將 React 組件渲染成靜態的 HTML 並分析生成的 HTML 結構。render
返回的 wrapper
與其餘兩個 API 相似。不一樣的是 render
使用了第三方 HTML 解析器和 Cheerio
。通常狀況下,shallow
就已經足夠用了,偶爾狀況下會用到 mount
。
這一步,咱們能夠建立一個 setup
函數來實現。
const setup = () => { // 模擬 props const props = { // Jest 提供的mock 函數 onAddClick: jest.fn() } // 經過 enzyme 提供的 shallow(淺渲染) 建立組件 const wrapper = shallow(<AddTodoView {...props} />) return { props, wrapper } }
Props
中包含函數的時候,咱們須要使用 Jest 提供的 mockFunction
這裏的 Case 根據咱們前面分析須要測試的點編寫。
Case1:測試組件是否正常渲染
describe('AddTodoView', () => { const { wrapper, props } = setup(); // case1 // 經過查找存在 Input,測試組件正常渲染 it('AddTodoView Component should be render', () => { //.find(selector) 是 Enzyme shallow Rendering 提供的語法, 用於查找節點 // 詳細用法見 Enzyme 文檔 http://airbnb.io/enzyme/docs/api/shallow.html expect(wrapper.find('input').exists()); }) })
寫完第一個測試用例以後,咱們能夠運行看看測試的效果。在 Terminal 中輸入 npm run test
,效果以下:
Case2: 輸入內容並敲下回車鍵,測試組件調用props的方法
it('When the Enter key was pressed, onAddClick() shoule be called', () => { // mock input 輸入和 Enter事件 const mockEvent = { keyCode: 13, // enter 事件 target: { value: 'Test' } } // 經過 Enzyme 提供的 simulate api 模擬 DOM 事件 wrapper.find('input').simulate('keyup',mockEvent) // 判斷 props.onAddClick 是否被調用 expect(props.onAddClick).toBeCalled() })
上面的代碼與第一個 case 多了兩點:
mockEvent
,用於模擬 DOM 事件Enzyme
提供的 .simulate(’keyup‘, mockEvent)
來模擬點擊事件,這裏的 keyup
會自動轉換成 React 組件中的 onKeyUp
並調用。咱們再運行 npm run test
看看測試效果:
通過上面兩個 Test Case 的分析,接下來的 Case3 和 Case4 思路也是同樣,具體寫法見代碼: __test__/component/AddTodoView.spec.js,這裏就不一一講解了。
因爲 Reducer 是純函數,所以對 Reducer 的測試很是簡單,Redux 官方文檔也提供了測試的例子,代碼以下:
import reducer from '../../reducers/todos' import * as types from '../../constants/ActionTypes' describe('todos reducer', () => { it('should return the initial state', () => { expect( reducer(undefined, {}) ).toEqual([ { text: 'Use Redux', completed: false, id: 0 } ]) }) it('should handle ADD_TODO', () => { expect( reducer([], { type: types.ADD_TODO, text: 'Run the tests' }) ).toEqual( [ { text: 'Run the tests', completed: false, id: 0 } ] ) expect( reducer( [ { text: 'Use Redux', completed: false, id: 0 } ], { type: types.ADD_TODO, text: 'Run the tests' } ) ).toEqual( [ { text: 'Run the tests', completed: false, id: 1 }, { text: 'Use Redux', completed: false, id: 0 } ] ) }) })
更多關於 Redux 的測試能夠看官網提供的例子:編寫測試-Redux文檔
在運行測試腳本過程,Jest
的錯誤提示信息友好,經過錯誤信息通常都能找到問題的所在。
同時 Jest
還提供了生成測試覆蓋率報告的命令,只須要添加上 --coverage
這個參數既可生成。不只會在終端中顯示:
並且還會在項目中生成 coverage
文件夾,很是方便。