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,我们此刻看到的目录结构是这样的,简简单单:

React单元测试——十八般兵器齐上阵,环境构建篇_第1张图片

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

会发现报错了:

React单元测试——十八般兵器齐上阵,环境构建篇_第2张图片

这是 phantomJS 的一个坑导致的—— phantomJS 不支持 Function.prototype.bind ,详情可见此issue。

解决方法也简单,把 karma-phantomjs 替换为 karma-phantomjs-shim 即可。

通过 npm 安装好 karma-phantomjs-shim 后我们修改 karma.conf.js 里的 frameworks 配置项:

        frameworks: [
            'mocha', 'phantomjs-shim'
        ],

然后重新执行 karma,会发现继续妥妥地报错:

React单元测试——十八般兵器齐上阵,环境构建篇_第3张图片

这是因为 karma 最终是将单元测试运行于一个客户端浏览器中的,而不是node里,而我们的测试模块又没有 require('assert') 的引用,客户端自然取不到assert对象了。

解决方法是使用 karma-chai,通过 npm 安装后进一步修改 karma.conf.js 里的 frameworks 配置项,加上 chai 插件:

        frameworks: [
            'mocha',  'chai',  'phantomjs-shim'
        ],

然后再执行 karma:

React单元测试——十八般兵器齐上阵,环境构建篇_第4张图片

666的~ 到此为止我们的全部配置都折腾完毕。在后续可以使用此方案对 src 目录下的全部组件进行简单的单元测试~ 更多有趣的配置或工具我们在后续的文章再做介绍。

最后依旧提醒一下,本文的示例可以从我的仓库上下载到,有兴趣的读者可以下载了自行研究~ 共勉~

React单元测试——十八般兵器齐上阵,环境构建篇_第5张图片

你可能感兴趣的:(React单元测试——十八般兵器齐上阵,环境构建篇)