搭建 Jest+ Enzyme 测试环境

1.为何要使用单元测试工具?

由于代码之间的相互调用关系,又但愿测试过程单元相互独立,又能正常运行,这就须要咱们对被测函数的依赖函数和环境进行mock,在测试数据输入、测试执行和测试结果检查方面存在不少类似性,测试工具正是为咱们在这些方面提供了方便。css

所谓单元测试也就是对每一个单元进行测试,通俗的将通常针对的是函数,类或单个组件,不涉及系统和集成。单元测试是软件测试的基础测试。html

2.React 的标配测试工具 Jest。

Jest主要有如下特色:node

      1.适应性:Jest是模块化、可扩展和可配置的。react

     2.沙箱和快速:Jest虚拟化了JavaScript的环境,能模拟浏览器,而且并行执行webpack

     3.快照测试:Jest可以对React 树进行快照或别的序列化数值快速编写测试,提供快速更新的用户体验。web

     4.支持异步代码测试:支持promises和async/awaitnpm

     5.自动生成静态分析结果:不只显示测试用例执行结果,也显示语句、分支、函数等覆盖率。json

JEST对比Mocha来讲,由于以下几个优势最后胜出:redux

     1.和React师出同门,FB官方支持api

     2.已经集成了测试覆盖率检查、mock等功能,不须要安装额外的库

     3.文档完备,官方提供了和babel、webpack集成状况下以及异步调用的测试解决方案

     4.官方提供snapshot testing解决方案

3.Jest + Enzyme的使用过程

1.安装

$ nam install —save-dev jest

若是须要在测试项目中使用babel,须要安装babel-jest

$ nam install —save-dev babel-jest

而后安装enzyme

$ npm install enzyme —save-dev

若是使用的react版本在13以上,还须要安装react-addons-test-utils

$ nam i —save-dev react-addons-test-utils

 

2.配置

JEST运行基础功能虽然无需配置,可是官方依然提供了配置选项来实现个性化需求。

package.json文件中配置jest的collectCoverageFrom参数,来指定检查全部须要测试的文件(不管源文件有没有被测试文件使用到)

coverageThreshold 参数来配置测试覆盖率。

 

jest.config.js 配置jest:

module.exports = {

  bail: true, //赶上 test feature, 则Stop running test, 默认值是false

  cacheDirectory: './node_modules/.cache', //测试缓存数据的存储位置

  testEnvironment: 'jsdom', //default brower-like enviroment, 若是你搭建了一个node service node-like enviroment

  coverageThreshold: { //测试覆盖率, 阈值不知足,就返回测试失败

    global: {

      branches: 90,

      functions: 90,

      lines: 90,

      statements: 90,

    },

  },

  coveragePathIgnorePatterns: [ //该路径下的测试,忽略在测试覆盖率上

    'build',

    '<rootDir>/src/shared/libs/url/',

  ],

  testRegex: 'test/.*\\.jsx?$', //要测试的文件目录及后缀

  testPathIgnorePatterns: [ //忽略该路径的文件测试

    '<rootDir>/node_modules/',

    '<rootDir>/build/',

    '<rootDir>/scripts/',

    '<rootDir>/api/',

    '<rootDir>/test/setup.js',

    '__mocks__',

  ],

  moduleFileExtensions: ['', 'json', 'js', 'jsx', 'less'], //测试模块中用到的文件的后缀名配置

  modulePaths: ['<rootDir>/src', '<rootDir>'],

  moduleNameMapper: {  //与测试无关的资源文件赞成mock 掉,这样在import 的时候就不会真的引入这些文件

    '^import?': '<rootDir>/build/jestImportMock.js',

    '\\.(css|less|gif|jpg|jpeg|png)$': '<rootDir>/build/jestStyleMock.js',

  },

  setupFiles: ['<rootDir>/test/setup.js'], //给每一个测试文件添加额外的配置

  transformIgnorePatterns: [ //测试过程不改变知足配置的文件

    '<rootDir>/node_modules/(?!(react-aaui|tempest\\.js)/)',

    'babel-runtime',

  ],

}

 

4.了解React官方测试工具库

react测试能够分为测试DOM结构 和测试Action和Reducer

React官方测试工具库提供两种测试形式:

1.Shallow Rendering 测试虚拟DOM的方法 

Shallow Rendering (浅渲染)指的是,将一个组件渲染成虚拟DOM对象,可是只渲染第一层,不渲染全部子组件,因此处理速度很是快。它不须要DOM环境,由于根本没有加载进DOM。

import TestUtils from 'react-addons-test-utils';

function shallowRender(Component) {

  const renderer = TestUtils.createRenderer();

  renderer.render(<Component/>);

  return renderer.getRenderOutput();

}

Shallow Rendering 函数,该函数返回的就是一个浅渲染的虚拟DOM对象。只有一层,不返回子组件。

2.DOM Rendering 测试真实DOM的方法

官方测试工具库的第二种测试方法,是将组件渲染成真实的DOM节点,再进行测试。这时就须要调用renderIntoDocument 方法。

import TestUtils from 'react-addons-test-utils';

import App from '../app/components/App';

const app = TestUtils.renderIntoDocument(<App/>);

renderIntoDocument 方法要求存在一个真实的DOM环境,不然会报错。所以,测试用例之中,DOM环境(即window, document 和 navigator 对象)必须是存在的

 

Enzyme库对官方测试库进行了封装,它提供三种方法:

import { shallow, mount, render } from ‘enzyme’

shallow 返回组件的浅渲染,对官方shallow rendering 进行封装

const wrapper = shallow(<Counter {...props} />)

expect(wrapper.find('button').exists()).toBeTruthy()

shallow 返回Counter 的浅渲染,而后调用find 方法查找 button 元素

关于find方法,有一个注意点,就是它只支持简单选择器,稍微复杂的一点的CSS选择器都不支持。

render 方法将React组件渲染成静态的HTML字符串,而后分析这段HTML代码的结构,返回一个对象。它跟shallow方法很是像,主要的不一样是采用了第三方HTML解析库Cheerio,它返回的是一个Cheerio实例对象。

const wrapper = render(<Counter {...props} />)

expect(wrapper.find('button').exists()).toBeTruthy()

render方法与shallow方法的API基本是一致的。

Enzyme的设计就是,让不一样的底层处理引擎,都具备一样的API

 

mount 方法用于将React组件加载为真实DOM节点。

const wrapper = mount(<Counter arithmetic={arithmetic})

 wrapper.find('button').simulate('click')

 

Enzyme的一部分API,你能够从中了解它的大概用法。

.get(index):返回指定位置的子组件的DOM节点

.at(index):返回指定位置的子组件

.first():返回第一个子组件

.last():返回最后一个子组件

.type():返回当前组件的类型

.text():返回当前组件的文本内容

.html():返回当前组件的HTML代码形式

.props():返回根组件的全部属性

.prop(key):返回根组件的指定属性

.state([key]):返回根组件的状态

.setState(nextState):设置根组件的状态

.setProps(nextProps):设置根组件的属性

 

toMatchSnapshot方法会去帮你对比此次将要生成的结构与上次的区别

 

测试 异步action

他的I/O可能依赖store.getState(),自身又会依赖异步中间件,这类使用原生js测试起来比较困难,咱们的目的能够设定为:当咱们触发一个action后,它经历了一个圈异步最终store.getAction中的action拿到的数据和咱们预期一致。所以咱们须要用到两个库:redux-mock-store 和 nock。

const mockStore = configureStore([thunk, promiseMiddleware]) //配置mock 的store,让他们有相同的middleware

afterEach(() => 

 nock.cleanAll()

)  //每执行完一个测试后,清空nock

 

const store = mockStore({

   router: {

      location: '/',

    },

})  //以咱们约定的初始state建立store,控制 I/O 依赖

 

const data = [

//接口返回的信息

{…

},

]

 

nock(API_HOST) //拦截请求返回的response

.get(`/api/…`) //拼接路由,须要在test.js中配置测试路径

.reply(200, {code: 0, data})

return store.dispatch(actions.getAll()).then(() => expect(store.getActions()).toMatchSnapShot())

 

1.用nock来mock拦截http请求结果,并返回咱们给定的response

2.用redux-mock-store 来mock store 的生命周期,须要预先把middleware配成和项目一致

3.describe会包含一些生命周期的api,好比所有测试开始作啥,单个测试结束作啥api,这里每执行完一个测试就清空nock

4.用了jest中的toMatchSnapShot api 来判断两个条件是否一致。

原本你要写成 expect(store.getActions()).toEqual({data …}) 你须要把equal 里的东西都描写具体,而toMatchSnapshot

可在当前目录下生成一个snapshot ,专门存放当前结果,写测试时看一眼是预期的就commit。若是改坏了,函数就不匹配snapshot了。

5.测试注意事项

1.拆分单元,关注输入输出,忽略中间过程。dom测试时只用确保正确调用了action函数,传参正确,而不用关注函数调用结果,置于action处理结果,reducer中对state的改变这些都留给action和reducer本身的单元测试区测。不要想着测试整个大功能的流程,不要有闭环的思想,单元测试须要保证的当前单元正常,对于每一个单元模块输入输出都正确,理论串联后一块儿使用闭环时也会正确。

 

2.多种状况的测试覆盖,若是不能保证测试的全面性,每种状况都覆盖到,那么这个测试就是个不敢依靠的不全面的测试。固然在实际项目中,可能由于时间、资源等问题,没法保证每种状况都测试到,而只测试主要的内容,这时候要作到内心有数,反正我是对于每一个测试都写注释的,交代清楚测试覆盖了哪些,还有哪些没有覆盖,须要其余手段保持稳定性。

 

3.关注该关注的,可有可无的mock掉。css、图片这种mock掉,http请求mock掉

 

4.本来不利于测试的代码仍是须要修改的,并不能为了原代码稳定不变,在测试时不敢动原代码。譬如函数不纯,没有返回值等。

相关文章
相关标签/搜索