React单元测试——十八般兵器齐上阵,环境构建篇
一个完整、优秀的项目往往离不开单元测试的环节,就 github 上的主流前端项目而言,基本都有相应的单元测试模块。
就 React 的项目来说,一套完整的单元测试能在在后续迭代更新中回归错误时候给与警示,但鉴于 React 本身的特殊性,我们又常常将其与 webpack 等工具相结合,其单元测试的部署相比常规的项目要折腾的多。
本文将作为 React 单元测试系列的开篇,和大家一同逐步构建其单元测试的环境。
你可以在我的仓库下载到本文的示例。

文件组织和配置
1. 目录结构
我们在项目根目录下新建 src 和 test 文件夹,前者用于存放我们编写的各 React 组件模块,后者则用于存放对应单元测试模块。
接着我们需要一个最本质的 package.json 文件,来描述项目的信息跟依赖模块,在根目录执行
npm init
然后分部输入相关信息(你也可以先一路回车,后续再修改),即可自动生成 package.json。
接着我们先通过 npm 安装 React 所需的两个模块—— react 及 react-dom:
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 目录下的全部组件进行简单的单元测试~ 更多有趣的配置或工具我们在后续的文章再做介绍。
最后依旧提醒一下,本文的示例可以从我的仓库上下载到,有兴趣的读者可以下载了自行研究~ 共勉~

React单元测试——十八般兵器齐上阵,环境构建篇的更多相关文章
- Maven系列第9篇:多环境构建支持,核心开发必备!
maven系列目标:从入门开始开始掌握一个高级开发所需要的maven技能. 这是maven系列第9篇. 整个maven系列的内容前后是有依赖的,如果之前没有接触过maven,建议从第一篇看起,本文尾部 ...
- react 单元测试 (jest+enzyme)
为什么要做单元测试 作为一个前端工程师,我是很想去谢单元测试的,因为每天的需求很多,还要去编写测试代码,感觉时间都不够用了. 不过最近开发了一个比较复杂的项目,让我感觉一旦项目大了.复杂了,而且还是多 ...
- Hyperledger Fabric 1.0 从零开始(三)——环境构建(内网/准离线)
有公网环境的服务器可以直接看 Hyperledger Fabric 1.0 从零开始(二)--环境构建(公网) ,本篇内容与上篇相似,只不过环境搭建需要在内网下,也就是网络被限制的情况下. 1:环境构 ...
- Hyperledger Fabric 1.0 从零开始(二)——环境构建(公网)
1:环境构建 在本文中用到的宿主机环境是Centos ,版本为Centos.x86_647.2,通过Docker 容器来运行Fabric的节点,版本为v1.0.因此,启动Fabric网络中的节点需要先 ...
- SSM框架整合环境构建——基于Spring4和Mybatis3
目录 环境 配置说明 所需jar包 配置db.properties 配置log4j.properties 配置spring.xml 配置mybatis-spring.xml 配置springmvc.x ...
- React 与 Redux 在生产环境中的实践总结
React 与 Redux 在生产环境中的实践总结 前段时间使用 React 与 Redux 重构了我们360netlab 的 开放数据平台.现将其中一些技术实践经验总结如下: Universal 渲 ...
- Cocos2d-x v3.1 GUI系统--环境构建(七)
Cocos2d-x v3.1 GUI系统--环境构建(七) 在使用Cocos2d-x的GUI系统时,由于生成的工程默认是没有将GUI系统所需的库导入到项目的,所以我们必须把库导入到工程中并对工程做一些 ...
- 如何从零搭建一个webpack+react+redux+react-redux的开发环境一入门
阅读本文章的时候,你要有一定的基础知识储备,简单的es6知识,模块化思想知识,js基础知识,node基础知识,react等 首先执行npm init,此时我的文件叫case; 下面安装一些需要的npm ...
- jQuery2.x源码解析(构建篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...
随机推荐
- .NetCore MVC中的路由(2)在路由中使用约束
p { margin-bottom: 0.25cm; direction: ltr; color: #000000; line-height: 120%; orphans: 2; widows: 2 ...
- Spring之旅(2)
Spring简化Java的下一个理念:基于切面的声明式编程 3.应用切面 依赖注入的目的是让相互协作的组件保持松散耦合:而AOP编程允许你把遍布应用各处的功能分离出来形成可重用的组件. AOP面向切面 ...
- 《Django By Example》第三章 中文 翻译 (个人学习,渣翻)
书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:第三章滚烫出炉,大家请不要吐槽文中 ...
- 运用php做投票题,例题
要求大概是这样的,有一个题目,题目下面是复选框,要求点完复选框提交后会变成进度条,各选项的进度条百分比,和投票数量 首先还是要在数据库建两张表,如下: 要完成这个题目,需要建两个页面 <!DOC ...
- pt-pmp
pt-pmp有两方面的作用:一是获取进程的堆栈信息,二是对这些堆栈信息进行汇总. 进程的堆栈信息是利用gdb获取的,所以在获取的过程中,会对mysql服务端的性能有一定的影响. 用官方的话说: Thi ...
- Android中点击事件的实现方式
在之前博文中多次使用了点击事件的处理实现,有朋友就问了,发现了很多按钮的点击实现,但有很多博文中使用的实现方式有都不一样,到底是怎么回事.今天我们就汇总一下点击事件的实现方式. 点击事件的实现大致分为 ...
- LINQ to SQL语句(7)之Exists/In/Any/All/Contains
适用场景:用于判断集合中元素,进一步缩小范围. Any 说明:用于判断集合中是否有元素满足某一条件:不延迟.(若条件为空,则集合只要不为空就返回True,否则为False).有2种形式,分别为简单形式 ...
- Hadoop
Hadoop应用场景 Hadoop是专为离线处理和大规模数据分析而设计的,它并不适合那种对几个记录随机读写的在线事务处理模式. 大数据存储:Hadoop最适合一次写入.多次读取的数据存储需求,如数据仓 ...
- 从贝叶斯到粒子滤波——Round 1
粒子滤波确实是一个挺复杂的东西,从接触粒子滤波到现在半个多月,博主哦勒哇看了N多篇文章,查略了嗨多资料,很多内容都是看了又看,细细斟酌.今日,便在这里验证一下自己的修炼成果,请各位英雄好汉多多指教. ...
- 写个Fragment方便的抽象基类 BaseFragment
package com.zb.zhihuianyang.base; import android.app.Activity; import android.os.Bundle; import andr ...