一个完整、优秀的项目每每离不开单元测试的环节,就 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 目录下的所有组件进行简单的单元测试~ 更多有趣的配置或工具咱们在后续的文章再作介绍。
最后依旧提醒一下,本文的示例能够从个人仓库上下载到,有兴趣的读者能够下载了自行研究~ 共勉~