一個完整、優秀的項目每每離不開單元測試的環節,就 github 上的主流前端項目而言,基本都有相應的單元測試模塊。html
就 React 的項目來講,一套完整的單元測試能在在後續迭代更新中迴歸錯誤時候給與警示,但鑑於 React 自己的特殊性,咱們又經常將其與 webpack 等工具相結合,其單元測試的部署相比常規的項目要折騰的多。前端
本文將做爲 React 單元測試系列的開篇,和你們一同逐步構建其單元測試的環境。vue
你能夠在個人倉庫下載到本文的示例。node
文件組織和配置react
1. 目錄結構webpack
咱們在項目根目錄下新建 src 和 test 文件夾,前者用於存放咱們編寫的各 React 組件模塊,後者則用於存放對應單元測試模塊。git
接着咱們須要一個最本質的 package.json 文件,來描述項目的信息跟依賴模塊,在根目錄執行github
npm init
而後分部輸入相關信息(你也能夠先一路回車,後續再修改),便可自動生成 package.json。web
接着咱們先經過 npm 安裝 React 所需的兩個模塊—— react 及 react-dom:npm
npm install --save-dev react react-dom
這裏順便說一聲,React 在 v0.14 開始把 react 模塊拆分紅了上述的兩個包,其中 react 模塊 中包含 React.createElement、React.createClass 等API,react-dom 模塊中則包含 ReactDOM.render 等API(更具體的能夠戳這裏瞭解)。
安裝完成後根目錄會生成存放各 npm 模塊的文件夾 node_modules,咱們此刻看到的目錄結構是這樣的,簡簡單單:
2. webpack 配置
咱們打算隨主流以ES6的形式來書寫腳本模塊,同時新版的 React 也已經把 JSX 的轉換權移交給 babel 之類的工具,因此咱們打算以 webpack 的形式來配置加載器跟打包。
若是你還不瞭解 webpack,請移步個人這篇入門文章。
先經過 npm 安裝 webpack(後續模塊的安裝方式再也不贅述):
npm install --save-dev webpack
咱們要搭建一個極其簡單的測試環境,因此暫時只需用到一個很簡單的webpack配置,因此咱直接在根目錄下新建一個 webpack.config.js:
var webpack = require('webpack'); module.exports = { entry: undefined, output: { pathinfo: true }, module: { //加載器配置 loaders: [ { test: /\.js$/, loader: 'babel-loader' } ] } };
注意咱們打算對全部 .js 文件配置一個 babel-loader 來轉換 JSX 跟 ES6,因此記得經過 npm 安裝上 babel-loader。
3. karma 配置
一個好的測試工具能大大提高你的工做效率,而做爲Angular團隊出品的 karma 是出衆的、最受歡迎的一款測試工具,它有以下特色:
1. cli 運行,webstorm下配合完美 2. 良好支持 mocha、jasmine 等測試框架 3. 支持多瀏覽器的測試 4. 生態好,插件多 5. 集成監控解放雙手,文件變化時自動啓測,相似gulp的watch功能
要留意的是 karma 的安裝最好是以全局的形式來安裝,這樣才能確保正常使用 karma 的cli功能(咱們後續將以 karma XXX 的形式來執行測試):
npm install karma -g
接着咱們在根目錄下新建 karma.conf.js 配置文件:
var isCI = process.env.CONTINUOUS_INTEGRATION === 'true'; var webpackConfig = require('./webpack.config.js'); module.exports = function(config) { config.set({ basePath: '', files: [ 'test/*.js' ], preprocessors: { 'test/*.js': ['webpack'] }, webpack: webpackConfig, webpackMiddleware: { noInfo: true }, port: 9876, colors: true, autoWatch: true, singleRun: isCI }); };
其中 isCI 變量用於判斷當前系統環境是否已默認支持持續集成(經過環境變量CONTINUOUS_INTEGRATION判斷,具體CI的變量名或值是什麼得依據具體狀況來定,譬如 vuejs 中使用的是CI_PULL_REQUEST),若沒開啓CI則將 singleRun 設爲false。
另外咱們在 preprocessors 作了定義,要求執行 test 目錄下的腳本時先經過 webpack 預處理(轉JSX、ES6),並在 webpack 配置項設定其配置爲咱們以前創建的 webpack.config.js 。
咱們打算使用 mocha 來做爲單元測試的框架(固然你也可使用 jasmine),而後使用 phantomjs 來做爲測試瀏覽器引擎。
因此先經過 npm 包安裝好這倆個模塊的 karma 插件:
npm install --save-dev karma-mocha karma-phantomjs-launcher
而後咱們進一步配置 karma.conf.js:
var isCI = process.env.CONTINUOUS_INTEGRATION === 'true'; var webpackConfig = require('./webpack.config.js'); module.exports = function(config) { config.set({ basePath: '', frameworks: [ 'mocha', 'phantomjs' ], files: [ 'test/*.js' ], preprocessors: { 'test/*.js': ['webpack'] }, webpack: webpackConfig, webpackMiddleware: { noInfo: true }, port: 9876, colors: true, autoWatch: true, browsers: ['PhantomJS', 'PhantomJS_custom'], customLaunchers: { //自定義瀏覽器啓動器 'PhantomJS_custom': { base: 'PhantomJS', options: { windowName: 'my-window', settings: { webSecurityEnabled: false } }, flags: ['--load-images=true'], debug: true } }, phantomjsLauncher: { // 資源(好比測試模塊)出錯時依舊保持phantom不退出 exitOnResourceError: true }, singleRun: isCI }); };
到了這一步,咱們先僞裝配置都已經摺騰完畢了(其實尚未),下面是新增測試模塊
4. 建立測試模塊
如今 src 目錄下尚未任何 React 組件,咱們建立一個 Alert.js:
import React from 'react'; const Alert = React.createClass({ render() { return ( <div {...this.props}> {this.props.children} </div> ); } }); export default Alert;
接着在 test 目錄下新增一個 Alert.js 文件,用於對上述的 src/Alert.js 組件進行簡單的單元測試:
import React from 'react'; import ReactTestUtils from 'react/lib/ReactTestUtils'; import Alert from '../src/Alert'; describe('Alert', () => { it('往頁面插入一段帶有strong標籤的組件', () => { let instance = ReactTestUtils.renderIntoDocument( <Alert> <strong>Message</strong> </Alert> ); assert.ok(ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'strong')); }); });
如上述代碼所示,咱們假設往<Alert>組件中放置了一個<strong>標籤並渲染到頁面上(這裏使用了 react/lib/ReactTestUtils 是咱們下篇文章要介紹的東西)。
因而咱們斷言頁面上經過此形式所掛載上去的 Alert 組件裏確定有一個 <strong> 標籤,若是找不到這個 <strong> 標籤則意味着該單元測試失敗。
如今咱們彷佛基本完成了所有的配置,執行 karma 啓動單元測試看一看:
karma start --browsers PhantomJS_custom
會發現報錯了:
這是 phantomJS 的一個坑致使的—— phantomJS 不支持 Function.prototype.bind ,詳情可見此issue。
解決方法也簡單,把 karma-phantomjs 替換爲 karma-phantomjs-shim 便可。
經過 npm 安裝好 karma-phantomjs-shim 後咱們修改 karma.conf.js 裏的 frameworks 配置項:
frameworks: [ 'mocha', 'phantomjs-shim' ],
而後從新執行 karma,會發現繼續妥妥地報錯:
這是由於 karma 最終是將單元測試運行於一個客戶端瀏覽器中的,而不是node裏,而咱們的測試模塊又沒有 require('assert') 的引用,客戶端天然取不到assert對象了。
解決方法是使用 karma-chai,經過 npm 安裝後進一步修改 karma.conf.js 裏的 frameworks 配置項,加上 chai 插件:
frameworks: [ 'mocha', 'chai', 'phantomjs-shim' ],
而後再執行 karma:
666的~ 到此爲止咱們的所有配置都折騰完畢。在後續可使用此方案對 src 目錄下的所有組件進行簡單的單元測試~ 更多有趣的配置或工具咱們在後續的文章再作介紹。
最後依舊提醒一下,本文的示例能夠從個人倉庫上下載到,有興趣的讀者能夠下載了自行研究~ 共勉~