Jest测试框架入门之快照测试(附踩坑指南)

一、快照测试简介

快照测试是用于确保某个组件的UI不会有意外的改变,与UI测试不同,快照测试不会对比样式文件,仅对比dom结构和节点参数。

进行快照测试最简单的做法需要引入渲染器 react-test-renderer

yarn add -D react-test-renderer

接下来我们写一个时间组件,使用的框架为umi3:

// MyDate/index.tsx

import { useState } from 'react';
import styles from './index.less';

const MyDate = () => {
  const [time, setTime] = useState(new Date().toLocaleTimeString())

  const updateTime = () => {
    setTime(new Date().toLocaleTimeString())
  }

  return (
    <div className={styles.root}>
      <div className='time'>当前时间为:{time}</div>
      <button className='btn' onClick={updateTime}>更新时间</button>
    </div>
  )
}

export default MyDate
// MyDate/index.less

.root {
  display: flex;
  flex-direction: column;
  align-items: center;

  :global {
    .time {
      font-size: 30px;
      margin-bottom: 20px;
    }
  }
}

写一个测试文件 MyDate.spec.tsx ,注意命名以 tsx 结尾 :

import renderer from 'react-test-renderer';
import MyDate from '../MyDate';

test('测试 MyDate 组件', () => {
  const tree = renderer.create(<MyDate />).toJSON();
  expect(tree).toMatchSnapshot();
})

运行结果:

在这里插入图片描述
遇到了很常见的错误,解析不了less模块。解决办法:官网指路

根据官网的方法安装并配置identity-obj-proxy之后,重新运行:

在这里插入图片描述
这种 Cannot read property ‘xxx’ of undefined的错,大部分原因都是安装的react版本和react-test-renderer版本不匹配导致,我项目里用的react版本为17,而react-test-renderer版本为18,现在降级为17:

yarn upgrade react-test-renderer@17.0.0

再次运行,报错消失,用例通过,且__test__目录下生成一个名为__snapshots__的文件夹,该文件夹下有一个快照文件:

// MyDate.spec.tsx.snap

exports[`测试 MyDate 组件 1`] = `
<div
  className="root"
>
  <div
    className="time"
  >
    当前时间为:
    下午5:54:02
  </div>
  <button
    className="btn"
    onClick={[Function]}
  >
    更新时间
  </button>
</div>
`;

再次运行:

在这里插入图片描述
这次不是报错,是用例失败了,这是由于我们每次运行的时候取得都是当前时间,和之前保存的快照对不上,这个时候我们可以通过mock useState来解决,修改测试文件如下:

import React from 'react';
import renderer from 'react-test-renderer';
import MyDate from '../MyDate';

const setState = jest.fn();
const useStateSpy = jest.spyOn(React, 'useState');
const useStateMock: any = (initState: any) => ['中午12:00:00', setState];
useStateSpy.mockImplementation(useStateMock);

test('测试 MyDate 组件', () => {
  const tree = renderer.create(<MyDate />).toJSON();
  expect(tree).toMatchSnapshot();
})

更新一下snap文件:

jest -u --testNamePattern='测试 MyDate 组件'

现在运行用例会通过,且snap文件变为:

exports[`测试 MyDate 组件 1`] = `
<div
  className="root"
>
  <div
    className="time"
  >
    当前时间为:
    中午12:00:00
  </div>
  <button
    className="btn"
    onClick={[Function]}
  >
    更新时间
  </button>
</div>
`;

二、Enzyme

如果需要进行组件行为监测,当 React 版本 <= 16 时可以使用Enzyme库。首先安装一些依赖:

yarn add -D enzyme @types/enzyme @wojtekmaj/enzyme-adapter-react-17

上面安装的@wojtekmaj/enzyme-adapter-react-17是非官方版的适配器,这也是为什么不推荐 React 版本 >= 17 时使用enzyme。

我们写一个非常简单的Counter组件:

import { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0)

  const handleClick = () => {
    setCount(count + 1)
  }

  const handleReset = () => {
    setCount(0)
  }

  return (
    <div>
      <button onClick={handleClick}>该按钮点击了{count}</button>
      <button onClick={handleReset}>重置点击次数</button>
    </div>
  )
}

export default Counter

写一个测试文件:

import Counter from '../Counter';
import { configure, shallow } from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';

configure({ adapter: new Adapter() })

test('测试 Counter 组件', () => {
  const c = shallow(<Counter />)

  expect(c.find('button').at(0).text()).toBe('该按钮点击了0次')

  c.find('button').at(0).simulate('click')
  c.find('button').at(0).simulate('click')

  expect(c.find('button').at(0).text()).toBe('该按钮点击了2次')

  c.find('button').at(1).simulate('click')

  expect(c.find('button').at(0).text()).toBe('该按钮点击了0次')
})

运行,通过!

三、react-testing-library(推荐)

React官网写了这么一句话:我们推荐使用 React Testing Library,它使得针对组件编写测试用例就像终端用户在使用它一样方便。

还有一篇文章也值得一看:Time to say goodbye - Enzyme.js

从npm的周下载量上可以看到react-testing-library是enzyme的两倍有余,看来enzyme的时代确实已经落幕了。下面我们也简单看一下react-testing-library的使用:

yarn add --dev @testing-library/react

我们还是以上面的Counter组件为例,重写一下测试文件:

import Counter from '../Counter';
import { render, screen } from '@testing-library/react';

test('测试 Counter 组件', () => {
  render(<Counter />)
  screen.debug()
})

运行一下,可能会出现以下报错:
1、Cannot find module ‘xxx’ from ‘xxx’

解决办法:将@testing-library/react降级到12.x.x版本

2、The error below may be caused by using the wrong test environment. Consider using the “jsdom” test environment.

解决办法:将 jest.config.js 中的 testEnvironment 改为 jsdom。

解决报错后,运行可看到通过screen.debug()在控制台打印出了dom结构。

写一个与上面类似的测试如下:

import Counter from '../Counter';
import { render, screen, fireEvent } from '@testing-library/react';


test('测试 Counter 组件', () => {
  render(<Counter />)
  // screen.debug()

  expect(screen.getAllByRole('button')[0].textContent).toBe('该按钮点击了0次');

  fireEvent.click(screen.getAllByRole('button')[0])

  expect(screen.getAllByRole('button')[0].textContent).toBe('该按钮点击了1次');

  fireEvent.click(screen.getAllByRole('button')[1])

  expect(screen.getAllByRole('button')[0].textContent).toBe('该按钮点击了0次');
})

运行,通过!

四、RTL的最佳实践

上面用RTL测试框架写了一个简单的测试用例,实际在项目里使用的话建议安装以下几个依赖,以便写出更规范、更高效的测试用例:

1、@testing-library/jest-dom,该库拓展了一些jest匹配器,可以使测试用例更具声明性且更易于阅读和维护。

yarn add -D @testing-library/jest-dom

2、@testing-library/user-event,该库是 fireEvent 的替代品,更接近用户的真实交互场景,尽可能用 userEvent 而不是 fireEvent。

yarn add -D @testing-library/user-event

3、eslint-plugin-testing-libraryeslint-plugin-jest-dom,这两个eslint插件会帮助我们在使用测试库编写测试时遵循最佳实践并预测常见错误。

yarn add -D eslint-plugin-testing-library eslint-plugin-jest-dom

OK,安装完毕之后,重新写一个测试页面:

import { useRef } from 'react';
/**
 * 这里使用的是antd-mobile v2.3.1版本
 */
import { Toast } from 'antd-mobile';
import styles from './index.less';

const Page = () => {
  const input = useRef<HTMLInputElement>(null)

  const submit = () => {
    if(input.current?.value.trim() !== '') {
      Toast.show('提交成功', 3)
    }else {
      Toast.show('姓名不能为空', 3)
    }
  }

  return (
    <div className={styles.page}>
      <div className='container'>
        <div>姓名:</div>
        <input ref={input} type="text" />
      </div>
      <button onClick={submit}>确认</button>
    </div>
  );
}

export default Page

测试文件如下:

import Page from '../index';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';

jest.useFakeTimers()

test('测试Page', () => {
  render(<Page />)
  // 点击确认
  userEvent.click(screen.getByRole('button', { name: '确认' }))
  // 弹窗文字为 '姓名不能为空'
  expect(screen.getByRole('alert').children[0].innerHTML).toBe('姓名不能为空')
  // 将时间快进3s
  jest.advanceTimersByTime(3000);
  // 弹窗不存在于文档中
  expect(screen.queryByRole('alert')).not.toBeInTheDocument()
  // 输入 '123' 并点击确认
  userEvent.type(screen.getByRole('textbox'), '123')
  userEvent.click(screen.getByRole('button', { name: '确认' }))
  // 弹窗文字为 '提交成功'
  expect(screen.getByRole('alert').children[0].innerHTML).toBe('提交成功')
})

运行,通过。

参考文章:使用 React Testing LIbrary 的 15 个常见错误

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Jest 是一个流行的 JavaScript 测试框架,可以用于编写单元测试、集成测试和端到端测试。下面是 Jest 单元测试入门的步骤: 1. 安装 Jest 使用 npm 安装 Jest:`npm install --save-dev jest` 2. 编写测试用例 在项目根目录下创建一个名为 `__tests__` 的文件夹,并在其中创建一个名为 `example.test.js` 的文件。在 `example.test.js` 文件中编写测试用例: ```javascript describe('示例测试', () => { test('测试1', () => { expect(1 + 1).toBe(2); }); test('测试2', () => { expect(true).toBeTruthy(); }); }); ``` 上面的代码定义了一个测试套件 `示例测试`,其中包含两个测试用例 `测试1` 和 `测试2`。每个测试用例都是一个函数,其中包含一个或多个 `expect` 语句,用于断言测试结果是否符合预期。 3. 运行测试 在命令行中输入 `npx jest` 命令,Jest 将自动查找项目中的测试用例并运行它们。如果所有测试用例都通过,Jest 将输出一个绿色的提示。 4. 高级配置 Jest 提供了丰富的配置选项,可以用于定制测试过程。例如,可以在 `package.json` 文件中添加以下配置: ```json { "jest": { "testEnvironment": "node", "testMatch": [ "**/__tests__/**/*.test.js" ], "coverageThreshold": { "global": { "branches": 80, "functions": 80, "lines": 80, "statements": 80 } } } } ``` 上面的配置指定了测试环境为 Node.js,测试文件必须位于 `__tests__` 文件夹中,并以 `.test.js` 结尾。还指定了代码覆盖率的阈值,如果代码覆盖率低于指定的阈值,Jest 将会提示测试失败。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值