文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Jest:给你的 React 项目加上单元测试

2024-12-01 12:47

关注

大家好,我是前端西瓜哥。

Jest 是一款轻量的 JavaScript 测试框架,它的卖点是简单好用,由 facebook 出品。本文就简单讲讲如何使用 Jest 对 React 组件进行测试。

为什么需要单元测试?

单元测试(Unit Testing),指的是对程序中的模块(最小单位)进行检查和验证。比如一个函数、一个类、一个组件,它们都是模块。

使用单元测试的优点:

Jest 判定测试脚本

Jest 需要 确认哪些是测试文件,默认判断测试文件的逻辑是:

可以通过设置  Jest 配置文件的 testMatch 或 testRegex 选项进行修改,或者 package.json 下的 "jest" 属性。

Jest 基本使用

我们先写一个简单的函数,作为被测试的模块。

function sum(a, b) {
return a + b;
}
export default sum;

然后我们用 Jest 来做测试。

import sum from './sum';

test('1 + 1 应该等于 2', () => {
expect(sum(1, 1)).toBe(2);
});

然后执行 jest 命令,得到测试结果。

test 方法创建了一个测试的作用域,该方法有三个参数:

  1. 测试的描述。
  2. 我们写测试代码的函数。
  3. 测试超时时间,默认为 5 秒,有些测试是异步的,我们需要等待。

test 方法有一个别名叫做 it,二者的功能是一致的,只是语义不同。通常用 test,但在某些情况下更适合用 it。这种情况就是 it 可以和描述语句拼成一句话的时候,比如:

it('should be true', () => { });

it 方法和后面的 should be true 拼成了一句主语为 it 的句子,语义更好。

我们通常使用 expect 来测试一个模块的逻辑是否符合预期。expect 会将模块返回的结果封装成一个对象,然后提供非常丰富的方法做测试。

比如 toBe 就可以做 Object.is 的对比测试。

// sum(1, 1) 的结果是否为 2
expect(sum(1, 1)).toBe(2);

expect 的实现思路大致为:

function expect(value) {
return {
toBe(comparedValue) {
if (Object.is(value, comparedValue)) {
// 记录测试成功
} else {
// 记录测试失败
}
},
// 其他 API
toBeTruthy() { },
// ...
}
}

利用了闭包。

还有一些其他的 toXX API,我们称为 matcher。比如:

更多 API 可以看文档:

​https://jestjs.io/docs/expect。​

你可以用 describe 方法将多个相关的 test 组合起来,这样能让你的测试用例更好地被组织,测试报告输出也更有条理。

describe('一个有多个属性的对象的测试', () => {
test('test 1', async () => {
expect(obj.a).toBeTruthy();
});

test('test 2', async () => {
expect(obj.b).toBeTruthy();
});
});

describe 里面可以嵌套 describe,即组里面还可以有组。

异步测试

如果使用异步测试,需要将 Promise 作为返回值。

test('请求测试', () => {
return getData().then(res {
expect(res.data.success).toBe(true);
})
})

或使用 async / await。

test('请求测试', async () => {
const res = await getData();
expect(res.data.success).toBe(true);
})

也支持回调函数风格的测试,你需要调用函数传入的 done 函数来表明测试完成:

test('异步测试', done => {
setTimeout(() {
expect('前端西瓜哥').toBeTruthy();
done();
}, 2000);
});

生命周期函数

beforeAll,在当前文件的正式开始测试前执行一次,适合做一些每次 test 前都要做的初始化操作,比如数据库的清空以及初始化。

beforeEach,在当前文件的每个 test 执行前都调用一次。

afterAll,在当前文件所有测试结束后执行一次,适合做一些收尾工作,比如将数据库清空。

afterEach,在当前文件的每个 test 执行完后都调用一次。

React Testing Library

本文不讲解安装和配置,我们先用 CreateReactApp 来搭建项目,并使用 TypeScript 模板。

yarn create react-app jest-app --template typescript

执行单元测试的命令为:

yarn test

CreateReactApp 内置了 Jest,但 Jest 本身并不支持 React 组件的测试 API,需要使用另外一个内置的 React Testing Library 库来测试  React 组件。

React Testing Library 是 以用户为角度 的测试库,能够模拟浏览器的 DOM,将 React 组件挂载上去后,我们使用其提供的一些模拟用户操作的 API 进行测试。

React Testing Library 的哲学是:

测试的写法越是接近应用被使用的方式,我们就越有自信将其交付给客户。

CreateReactApp 预置模板的 App.test.tsx 使用了 React Testing Library。

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

test('renders learn react link', () => {
render(<App);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

Enzyme

另一种比较流行的测试 React 组件的框架是  Enzyme,它的 API 简洁优雅,能够用类似 JQuery 的语法,对开发非常友好。Enzyme 由 Airbnd 出品,但目前已经不怎么维护了。

为此,你需要装一些包:

yarn add -D enzyme enzyme-adapter-react-16

如果你使用了 TS,你还得补上类型声明。

yarn add -D @types/enzyme @types/enzyme-adapter-react-16

示例:

import Enzyme, { shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import Button from '../button';
Enzyme.configure({ adapter: new Adapter() });
it('Button with children', () => {
const text = 'confirm';
const btn = shallow(<Button>{text}Button>);
expect(btn.text()).toBe(text);
});

目前(2022.10.25) enzyme 官方只支持到 React 16,Enzyme 已死:

​https://dev.to/wojtekmaj/enzyme-is-dead-now-what-ekl。​

使用 Jest 测试 React 组件

我们先实现一个简单的 Button 组件。

import { CSSProperties, MouseEvent, FC } from 'react';
import classNames from 'classnames';
import './style.scss';

const clsPrefix = 'xigua-ui-btn';

export type ButtonProps = {
type?: 'primary' | 'default'
size?: 'large' | 'middle' | 'small';
disabled?: boolean;
children?: React.ReactNode;
onClick?: (event: MouseEvent) => void;
style?: CSSProperties;
className?: string;
}

const Button: FC<ButtonProps> = (props) => {
const {
type = 'default',
size = 'middle',
disabled = false,
children,
onClick,
style,
className,
} = props;

const mixedClassName = classNames(
clsPrefix,
`${clsPrefix}-${type}`,
`${clsPrefix}-${size}`,
className
);

return (
<button
style={style}
className={mixedClassName}
disabled={disabled}
onClick={onClick}
>
{children}
button>
);
};

export default Button;

然后我们创建一个 button.test.tsx 测试文件。我们使用 React Testing Library。

我们写个测试。

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

test('Button with children', () => {
const text = 'confirm Btn';
render(<Button>{text}Button>);

screen.debug();
});

render 方法会将 React 组件挂载到虚拟的文档树上。screen.debug() 用于调试,能让我们看到虚拟树的完整结构。

<body>
<div>
<button
class="xigua-ui-btn xigua-ui-btn-default xigua-ui-btn-middle"
>
confirm Btn
button>
div>
body>

测试 Button 的文本内容是否正常显示:

test('Button with children', () => {
const text = 'confirm Btn';
// 渲染 Button 组件
render(<Button>{text}Button>);

// 找到内容为 text 的元素
const BtnElement = screen.getByText(text);
// 测试元素是否在 Document 上
expect(BtnElement).toBeInTheDocument();
});

测试 Button 的 onClick 能否正常触发:

test('Button click', () => {
let toggle = false;
render(<Button onClick={() => { toggle = true; }} />);

// 找到第一个 button 元素,然后触发它的点击事件
fireEvent.click(screen.getByRole('button'));
// 看看 toggle 变量是否变成 true
expect(toggle).toBe(true);
});

测试 Button 的 className 是否成功添加:

test('Button with custom className', () => {
const customCls = 'customBtn';
render(<button className={customCls} />);

// 找到按钮元素
const btn = screen.getByRole('button');
// 元素的 className 列表上是否有我们传入的 className
expect(btn).toHaveClass(customCls);
});

源码:

​https://github.com/F-star/xigua-ui/blob/main/src/components/button/​tests/button.test.tsx。

执行 yarn test :

结尾

为了让代码更健壮,做模块的单元测试还是有必要的,Jest 作为流行的测试库值得一试。

来源:前端西瓜哥内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯