react 如何进行单元测试

506 阅读5分钟

react 如何进行单元测试

​ TDD是测试驱动开发的缩写,它指的是设计范例,其中软件组件测试用于指导整个开发过程。要了解TDD的工作原理,首先,最好定义其重要概念之一-单元测试。引用 《 测试驱动开发》这句话作为文章开篇。之前虽也在开发中写单测,但并未理解单测其真正意义,看完此书,对单测有新一步理解,TDD,我的理解就是一种开发的循环或者开发模式,先写测试代码,再用最小的代码实现这个测试,再继续写测试代码,继续用最小的代码实现。当实现所有的测试用例,项目也就开发结束。实际中可能会浪费大量的时间来编写测试用例和维护,对于经常性变动的代码,尤为耗费精力,对于一些基础建设,公共基础组件库来说比较合适,但对于整体项目而言可能会延长整个项目的开发周期。

一、create-react-app项目单测

目前新版本的脚手架内置了 @testing-library/react + jest的单测配置,官方的说法是:测试将与实际的DOM节点一起使用,而不是处理呈现的React组件的实例。该库提供的实用程序可以方便地以与用户查询方式相同的方式查询DOM。通过标签文本查找表单元素(就像用户看到的一样),从文本中查找链接和按钮(就像用户看到的一样)。它还公开了一种建议的方法,data-testid以“ a逃生阴影”的方式查找 那些文本内容和标签不合理或不实际的元素。该库鼓励您的应用程序更易于访问,并允许您使测试更接近于以用户将要使用的组件的方式进行使用,这使您的测试使您更有信心在实际用户使用该应用程序时将其运行。Testing Library 使用的是 testing-library/jest-dom,可以在文档中查看断言,与jest还是有些区别

1.项目配置

1.1 @testing-library/react + jest

​ 脚手架内置,不需要单独配置

1.2. enzyme + jest

需要安装 enzyme enzyme-adapter-16(react 16)/ @wojtekmaj/enzyme-adapter-react-17 (react 17)

import Enzyme from "enzyme";
import Adapter from "enzyme-adapter-react-16";    // react 16
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';   // react 17
Enzyme.configure({ adapter: new Adapter() });

2.测试用例比较

1.编写组件

主要针对快照测试、UI测试、事件测试、异步测试这几个方面来进行

TestElement.js

import React, { useState } from 'react'

const TestElements = () => {
    const [counter, setCounter] = useState(0)

    return (
        <>
            <h1 data-testid="counter" className=''>{counter}</h1>
            <button data-testid="button-up" onClick={() => setCounter(counter + 1)}> Up</button>
            <button disabled data-testid="button-down" onClick={() => setCounter(counter - 1)}>Down</button>
        </>
    )
}

export default TestElements

TestElement 组件主要是进行快照测试,UI测试和事件测试对比

http.js

import axios from 'axios'

export default {
    getData() {
        return new Promise((resolve, reject) => {
            axios.get('https://www.fastmock.site/mock/232e23cfab0dad95b6d1c4d02f82ba6d/swb/swb1')
                .then(function (response) {
                    resolve(response.data.data.list)
                })
                .catch(function (error) {
                    reject(error)
                })
        })
    }
}

http.js 进行异步测试对比

2.测试用例对比
2.1使用enzyme进行单元测试
import React from 'react'
import {mount, configure, render} from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import TestElements from "../TestElements";
import httpApi from '../http'


configure({adapter: new Adapter()});
let taskList = []

describe('单元测试对比', () => {
    it('快照对比', () => {
        const wrapper = render(<TestElements/>)
        expect(wrapper).toMatchSnapshot()
    })
    it('异步接口', async () => {
        taskList = await httpApi.getData();                 //请求数据
        expect(taskList.constructor).toBe(Array);            //判断数据类型为数组

    });

    it('dom-事件测试', () => {
        const wrapper = mount(<TestElements/>)
        wrapper.find('.test-button-up').at(0).simulate('click');  //点击事件
        expect(wrapper.find('.test-count').at(0).text()).toEqual('1')
    })

    it('dom操作', () => {
        const wrapper = mount(<TestElements taskList={taskList}/>)        //将获取的数据导入组件
        const testElements = wrapper.find('.test-item')
        expect(testElements.length).toBe(taskList.length);                 //判断渲染的节点是否正确
        expect(testElements.at(0).find('p').at(0).text()).toBe(taskList[0].name)            //判断渲染的节点内容是否正确
        expect(Number(testElements.at(0).find('p').at(1).text())).toBe(taskList[0].age)            //判断渲染的节点内容是否正确
    })
})

2.2 使用@testing-library/react进行测试

import React from 'react';
import "@testing-library/jest-dom/extend-expect";
import {render, cleanup, fireEvent} from '@testing-library/react';
import TestElements from '../components/TestElements'
import httpApi from '../http'

afterEach(cleanup);
let taskList = []

it('快照对比', () => {
    const {asFragment} = render(<TestElements/>)
    expect(asFragment()).toMatchSnapshot()
})
it('异步测试', async () => {
    taskList = await httpApi.getData();                 //请求数据
    expect(taskList.constructor).toBe(Array);            //判断数据类型为数组
});

it('dom-事件测试', () => {
    const {getByTestId} = render(<TestElements/>)
    fireEvent.click(getByTestId('button-up'))               //点击事件
    expect(getByTestId('counter')).toHaveTextContent('1')
})

it('dom-ui测试', () => {
    const {getByTestId} = render(<TestElements taskList={taskList}/>)
    const wrapper = getByTestId('test-list').children
    expect(wrapper.length).toBe(taskList.length)
    expect(wrapper[0].children[0]).toHaveTextContent(taskList[0].name)
    expect(wrapper[0].children[1]).toHaveTextContent(taskList[0].age)

})

通过测试用例可以看到,两者的使用方式还是差别比较大的:

​ a. @testing-library/react 用render来渲染组件,@testing-library/react 提供的api 在获取dom节点及相关操作时不够方便

​ b. enzyme有shallow、mount、render三种渲染方式提供了比较多的dom操作api

从官网的描述来看:

​ a. @testing-library/react

​ 用于通过查询DOM节点并与之交互(无论是使用JSDOM/ Jest模拟还是在浏览器中进行测试)来测试网页。它提供的主要实用程序涉及以类似于用户在页面上查找元素的方式查询节点的DOM。通过这种方式,该库有助于确保您的测试使您确信应用程序在实际用户使用它时可以正常工作。

​ b. enzyme

​ Enzyme是用于React的JavaScript测试实用程序,可以更轻松地测试React组件的输出。您还可以根据给定的输出进行操作,遍历并以某种方式模拟运行时。通过模仿jQuery的DOM操作和遍历的API,Enzyme的API意味着直观且灵活。

​ 或许是之前一直用enzyme,没有使用过@testing-library/react ,个人感觉enzyme在操作dom层面还是比较灵活,不需要设置额外的标记也能直接编写测试用例