基于react的前端单元测试

1,177 阅读4分钟

基于react的前端单元测试

一、为什么需要单元测试

  • 降低bug率:通过单元测试能更早的发现问题,减少线上出现的bug率
  • 提升代码能力:单元测试可以让开发者去思考代码的逻辑,从而提高编程能力
  • 提高信心:如果单元测试都能通过的话,无形中也会提高开发者的信心

二、单元测试概念

单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。 在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。

三、如何去做单元测试

单元测试的原则

  • F-FAST(快速原则):单元测试应该是可以快速运行的,在各种测试方法中,单元测试的运行速度是最快的,通常应该在几分钟内运行完毕
  • I-Independent(独立原则):单元测试应该是可以独立运行的,单元测试用例互相无强依赖,无对外部资源的强依赖
  • R-Repeatable(可重复原则):单元测试应该可以稳定重复的运行,并且每次运行的结果都是相同的
  • S-Self Validating(自我验证原则):单元测试应该是用例自动进行验证的,不能依赖人工验证
  • T-Timely(及时原则):单元测试必须及时的进行编写,更新和维护,以保证用例可以随着业务代码的变化动态的保障质量

常用的单元测试工具

  • 测试框架:Mocha, Jasmine, Jest, Cucumber
  • 提供断言:Chai, Jasmine, Jest, Unexpected
  • 生成,展示测试结果:Mocha, Jasmine, Jest, Karma
  • 生成测试覆盖率报告:Istanbul, Jest, Blanket

四、单元测试的环境配置

第一步:创建react项目

npx create-react-app react-test
cd react-test
yarn start

第二步:安装Jest

yarn add -D Jest

注意:只安装Jest,然后在.test.js文件里面如果需要import 文件的时候这里会出错,因为Jest默认使用的是CommonJS来导入导出的,因此需要babel来让它支持ESModule的方式。

npm install --save-dev @babel/core @babel/preset-env @babel/preset-react

根目录下创建babel.config.js

module.exports = {
  presets: [
    ["@babel/preset-env", { targets: { node: "current" } }],
    "@babel/preset-react",
  ],
};

第三步:配置Jest

npx jest --init

生成jest.config.js,同时查看package.json发现scripts的命令里面多了个test,后面每次写好测试文件的时候,执行npm run test就可以啦。
jest常用API
jest.config.js里面常用的各个参数含义参考链接

第四步:写个简单的demo测试下

首先写个Counter组件

export default class Counter {
    constructor() {
        this.number = 0
    }
    add() {
        this.number += 1;
    }
    minus() {
        this.number -= 1
    }
}

接着写个测试Counter的测试文件

import Counter from '../components/Counter'
const counter = new Counter()
test('测试Counter中的add方法',()=>{
    counter.add()
    expect(counter.number).toBe(1)
})
test('测试Counter中的minus方法',()=>{
    counter.minus()
    expect(counter.number).toBe(0)
})

最后运行npm run test,结果如下:

截屏2022-03-15 上午11.38.20.png

五、react项目的单元测试

组件测试

一般组件我们是通过生成组件快照来测试的

1. 安装react-test-renderer,用这个插件来渲染快照
yarn add --dev react-test-renderer
2. 组件
import React, { useState } from 'react'
const STATUS = {
    hovered: 'hovered',
    normal: 'normal'
}
const Link = ({page, children})=>{
    const [status, setStatus] = useState(STATUS.normal)
    const onMouseEnter = ()=>{
        setStatus(STATUS.hovered)
    }
    const onMouseLeave = ()=>{
        setStatus(STATUS.normal)
    }
    return (
        <a 
            className={status} 
            href={page || '#'}
            onMouseEnter={onMouseEnter}
            onMouseLeave={onMouseLeave}
        >
            {children}
        </a>
    )
}
export default Link
3. 测试组件以及组件函数
import React from 'react'
import renderer from 'react-test-renderer'
import Link from '../components/Counter'
test('Link changes the class when hovered', ()=>{
    const component = renderer.create(
        <Link page='http://www.baidu.com'>百度</Link>
    )
    let tree = component.toJSON()
    expect(tree).toMatchSnapshot()
    tree.props.onMouseLeave()
    tree = component.toJSON()
    expect(tree).toMatchSnapshot()
})
4. 完整代码示列链接
5. 函数组件测试

针对react hooks的测试可以使用React-hooks-testing-library,这个包对react 版本是有要求的,需要react是16.9.0及以上。

函数测试

同步函数测试

就是步骤四的最后demo所示,获取的组件实例之后直接调用方法看方法所得结果是否符合预期即可

异步函数测试

异步函数主要有两个方式可以进行测试,第一个是用done,第二个是利用promise

import React from 'react'
const Counter = ({page, children})=>{
    const onClick = (cb)=>{
        setTimeout(()=>{
            cb('您好呀')
        }, 1000)
    }
    return (
        <div onClick={onClick}>测试按钮呀</div>
    )
}
export default Counter
import React from 'react'
import renderer from 'react-test-renderer'
import Counter from '../components/Counter'
test('Link changes the class when hovered', (done)=>{
    const component = renderer.create(<Counter />)
    let tree = component.toJSON()
    tree.props.onClick((val)=>{
        expect(val).toBe('您好呀')
        done()
    })
})