1 Enzyme 是什么?

Enzyme 是一类大分子生物催化剂。酶能加快化学反应的速度(即具有催化作用)
— wikipedia

Airbnb开源的 React 测试类库 Enzyme 提供了一套简洁强大的 API,并通过 jQuery 风格的方式进行DOM 处理,开发体验十分友好。不仅在开源社区有超高人气,同时也获得了React 官方的推荐。
Airbnb.io

Jest 我们上一篇已经说过了,今天主角是Enzyme , 如字面意思,Enzyme 是酶、有催化作用。那么在React 组件测试中和Jest 产生了什么化学?反应呢?


2 课前准备

2.1 项目

这里我们使用 create-react-app 初始化一个项目

npm i -g create-react-app
npx create-react-app enzyme-in-action --use-npm
cd enzyme-in-action
npm start 
复制代码

会自动打开浏览器将看到这个页面。

这个是 create-react-app 起手式,如果不太了解建议 Create a New React App – React 了解下。

2.2 准备jest 和 enzyme配置

在::package.json:: 中修改 scripts

- "test": "react-scripts test",
+ "test": "jest",
复制代码

在::package.json:: 中增加jest配置

"jest": {
  "transform": {
    "^.+\\.jsx?$": "babel-jest",
    "^.+\\.svg$": "jest-svg-transformer"
  },
  "moduleNameMapper": {
    "\\.(css|less)$": "identity-obj-proxy"
  }
}
复制代码

安装依赖包

npm i -D jest babel-jest babel-core babel-preset-env babel-preset-react enzyme enzyme-adapter-react-16 jest-svg-transformer identity-obj-proxy
复制代码

jest-svg-transformeridentity-obj-proxy 是为了保证 jsx 文件中的
import logo from './logo.svg' import './App.css' 被正常渲染出来

2.3 背景知识

  1. Enzyme 它提供三种测试方法:
    • ::shallow::
    • ::render::
    • ::mount::
  2. wrapper wrapper是enzyme包装好的类,以供api使用
  3. shallow 在单元测试的过程中,浅渲染将一个组件渲染成虚拟DOM对象,并不会渲染其内部的子组件,也不是真正完整的React Render,无法与子组件互动。

3 起手式

修改create-react-app 为我们生成好的::src/App.test.js::

import React from 'react'
import App from './App'
import { configure, shallow } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'

configure({ adapter: new Adapter() })

describe(`<App />`, () => {
  it('should render App', () => {
    const warpper = shallow(<App />)
    console.log(warpper.debug())
  })
})
复制代码

npm run test

warpper.debug() 已经输出渲染的Dom,这个 warpper用来我们后面,用jQuery语法来测试。


4 find测试节点

在::app.js::中增加

  <img src={logo} className="App-logo" alt="logo" />
+ <h1>Welcome to React</h1>
  <p>
复制代码

现在我们来正式写单测 ::app.test.js::

describe(`测试<App />`, () => {
  const warpper = shallow(<App />)

  it('1. 包含一个p标签', () => {
    expect(warpper.find('p').length).toBe(1)
  })

  it('2. class为"App-link"的元素能正常被渲染', () => {
    expect(warpper.find('.App-link').exists()).toBeTruthy()
  })

  it('3. header的class为“App-header”', () => {
    expect(warpper.find('header').hasClass('App-header')).toBeTruthy()
  })

  it('4. header有3个子元素', () => {
    expect(warpper.find('header').children().length).toBe(4)
  })

	it('5. 测试H1标签的内容', () => {
    expect(warpper.find('h1').text()).toBe('Welcome to React')
  })

  it('6. 测试image标签class', () => {
    expect(warpper.find({ alt: 'logo' }).hasClass('App-logo')).toBeTruthy()
  })
})
复制代码

写过jQuery 的同学有没有很熟悉?这里不过多解释。

这里推荐一个VsCode插件 Jest 在每个单元测试前面会有一个icon标示当前是否过测。


5 使用snapshots 测试组件

按照find的方法测试组件,需要写多少单元测试,jest 为我们提供了一种快照的方式,来对比组件的变化。 安装依赖:

npm i -D enzyme-to-json
复制代码

在::app.test.js::中增加测试

  import Adapter from 'enzyme-adapter-react-16'
+ import toJson from 'enzyme-to-json'
复制代码
describe(`测试<App /> snapshots`, () => {
  const tree = shallow(<App />)

  it('1. 匹配快照', () => {
    expect(toJson(tree)).toMatchSnapshot()
  })
})
复制代码

npm run test 在命令行中会有

 › 1 snapshot written.
Snapshot Summary
 › 1 snapshot written from 1 test suite.
复制代码

这时候我们看工程目录
增加了 src/__snapshots__/App.test.js.snap这个就是jest的快照。jest原生的快照比较复杂, enzyme-to-json为我们做了简化。

第一次写toMatchSnapshot 的时候,被创建。当我们修改测试的时候它被更新。
我们来尝试修改一下react 组件 看会有什么事情发生

修改::app.js::

- <h1>Welcome to React</h1>
+ <h1>Welcome to React Jest Enzyme</h1>
复制代码

npm run test 在命令行中会有 错误信息,告诉你哪里发生了变化

tips: 更新快照的命令:jest --updateSnapshot 在watch 的情况下 按u 更新快照,如果你用的是vscode 那么修改组件的时候会提醒你是否更新。

6 测试含有“props”的组件

首先增加一个<Link>组件 ::App.js::

export class Link extends PureComponent {
  render() {
    const { hide, address } = this.props
    return hide ? null : <a href={address}>Click</a>
  }
}
复制代码

在::app.test.js::中增加测试

describe(`测试<Link />`, () => {
  it('1. 测试link组件', () => {
    const warpper = shallow(<Link address="https://www.google.com" />)
    expect(warpper.props().href).toBe('https://www.google.com')
  })

  it('2. 测试设置Link props', () => {
    const warpper = shallow(<Link />)
    expect(warpper.find('a').length).toBe(1)
    warpper.setProps({ hide: true })
    expect(warpper.find('a').length).toBe(0)
  })
})
复制代码
  1. warpper.props()的结果为
{ href: 'https://www.google.com', children: 'Click' }
复制代码
  1. 在case :2. 测试设置Link props 中用warpper.setProps() 动态的给<Link />设置参数验证组件是否被正常渲染

X 未完待续…

后面还有 :如何测试state ,生命周期,等等。 Jest 没写尽兴,再整理一篇进阶的文章,有小伙伴不清楚什么是集成测试和单元测试,后面整理一篇cypress相关的文章。 秋日的北京周末,开始起雾霾,周末陪周老师备考高级教师资格证,才能坐电脑前写这么长时间。

2010年底来北京,一晃8年过去了。刚来北京的时候无知但无畏,到现在无味且无为。纪念一下这个心情复杂的时刻。望以后无论在什么地方,坦然以对。
— 今天的豆瓣日记
来自豆友分享的专辑? 空中 ベスト・オブ (豆瓣) 灰常好听,虽然夜已深,还是元气满满,开始写第三篇。

技术能力有限,有什么不妥之处请指正。 有什么关于前端方面有疑惑?的方面,可以留言交流,会放到后面的写作计划中。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐