chenxiaochun / blog

🖋️ChenXiaoChun's blog
181 stars 15 forks source link

编写jest单元测试有感 #66

Open chenxiaochun opened 5 years ago

chenxiaochun commented 5 years ago

编写单元测试时要尽量严谨

下面是一个页面的index.jsx代码。一般在index.jsx文件中,我通常仅用它来描述当前页面结构,不会在其中编写任何具体的逻辑。这样在编写单元测试时,仅需要测试页面的渲染结构是否正确即可。 通过代码可以看出,页面渲染时,Card组件中必须要渲染三个组件:OperationListCreateModal,并且Card的 title 属性上还要渲染一个CardTitle组件。

源代码:

import React from 'react'
import { Card } from 'antd'
import CardTitle from 'component/CardTitle'
import Operation from './Operation'
import List from './List'
import CreateModal from './Create'

const SwitchManage = () => (
  <Card title={<CardTitle />} bordered={false}>
    <Operation />
    <List />
    <CreateModal />
  </Card>
)

export default SwitchManage

测试代码:

import React from 'react'
import { shallow } from 'enzyme'
import { Card } from 'antd'
import SwitchManage from 'page/SwitchManage'
import Operation from 'page/SwitchManage/Operation'
import List from 'page/SwitchManage/List'
import CreateModal from 'page/SwitchManage/Create'
import CardTitle from 'component/CardTitle'

describe('page/SwitchManage', () => {
  it('测试页面可完整渲染', () => {
    const app = shallow(<SwitchManage />)
    expect(app.find(Card)).toHaveLength(1)
    expect(app.contains(<Operation />)).toBeTruthy()
    expect(app.contains(<List />)).toBeTruthy()
    expect(app.contains(<CreateModal />)).toBeTruthy()

    // 测试可完整渲染Card title属性
    const title = app.find(Card).prop('title')
    expect(title.type).toEqual(CardTitle)
  })
})

在测试代码的顶部引入相关的文件。因为我只是想测试页面的结构,并不期望它进行深层次的渲染,所以这里使用了shallow方法。

首先测试是否成功渲染了Card组件,这里使用了findtoHaveLength方法。也就是说只要在 app 中能找到一个Card组件,我们就认为它被成功渲染了。

然后我们期望它能够成功渲染OperationListCreateModal三个组件。这里我使用了contains方法,而不是上面的findtoHaveLength。之所以这样写,是因为这三个组件必须是 app 的直接子元素,而不能是它的任意一级子元素。而如果你还是使用findtoHaveLength的话,单元测试肯定也能跑过去,但它和我们代码所表达的意义就不相符了。

写到这里有的同学可能会说,那为什么在最开始测试Card组件的地方为什么不用contains方法呢?这是因为 app 的根组件就是Card,它和自己本身并没有任何的包含关系,因此这里无法使用contains方法。

如果,你就是想用contains方法也是可以的。只需要将这里const app = shallow(<SwitchManage />)改成const app = shallow(<div><SwitchManage /></div>),外面再套一层标签即可。