Jest进阶知识:React组件的单元测试

news2024/11/5 13:31:13

在现代前端开发中,组件是构建应用程序的基本单元。一个组件不仅拥有完整的功能,还能极大地提高代码的复用性。因此,在进行单元测试时,对重要组件进行测试是必不可少的。

Testing Library

Testing Library 是一个专门用于测试 Web 组件的工具库,其设计理念是“测试组件的行为而不是实现细节”。通过 Testing Library 提供的一系列 API,可以模拟浏览器中的用户交互方式,使测试更加贴近真实使用场景。

Jest 与 Testing Library 的关系

  • Jest:一个完整的测试框架,提供了匹配器、mock 库、断言工具等,旨在提供一个全面的测试工具链。
  • Testing Library:一个测试工具库,专注于测试组件的行为。它可以与各种框架结合使用,提供了一组用于测试 React 组件的工具,如 renderscreenfireEvent 等。

常用的 Testing Library 扩展库

  • @testing-library/react:提供了一组用于测试 React 组件的工具,如 renderscreenfireEvent
  • @testing-library/jest-dom:提供了一组 Jest 断言方法,用于测试 DOM 元素的状态和行为,如 toBeInTheDocumenttoHaveTextContent 等。
  • @testing-library/user-event:提供了一组用于模拟用户行为的工具,如 typeclicktab 等。

核心 API

render 方法

render 方法接收一个组件作为参数,将其渲染为 DOM 元素,并返回一个包含重要属性的对象:

  • container:渲染后的 DOM 元素,可用于模拟用户行为或进行断言验证。
  • baseElement:整个文档的根元素 <html>
  • asFragment:将渲染后的 DOM 元素转换为 DocumentFragment 对象,便于进行快照测试。
  • debug:在控制台输出渲染后的 DOM 元素的 HTML 结构,便于调试。

screen 对象

screen 对象封装了一系列常用的 DOM 查询和操作函数:

  • getByLabelText:根据 <label> 元素的 for 属性或内部文本,获取与之关联的表单元素。
  • getByText:根据文本内容获取元素。
  • getByRole:根据 role 属性获取元素。
  • getByPlaceholderText:根据 placeholder 属性获取表单元素。
  • getByTestId:根据 data-testid 属性获取元素。
  • queryBy*:类似于 getBy*,但当元素不存在时返回 null 而不是抛出异常。

测试组件示例

示例一:隐藏消息组件

import { useState } from "react";

function HiddenMessage({ children }) {
    const [isShow, setIsShow] = useState(false);
    return (
        <div>
            <label htmlFor="toggle">显示信息</label>
            <input
                type="checkbox"
                name="toggle"
                id="toggle"
                checked={isShow}
                onChange={(e) => setIsShow(e.target.checked)}
            />
            {isShow ? children : null}
        </div>
    );
}

export default HiddenMessage;

该组件接收一个子组件,并根据复选框的状态决定是否显示子组件。以下是对应的测试代码:

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

test("能够被勾选,功能正常", () => {
    const testMessage = "这是一条测试信息";
    render(<HiddenMessage>{testMessage}</HiddenMessage>);
    // 初始状态下,信息不应显示
    expect(screen.queryByText(testMessage)).toBeNull();
    // 模拟点击复选框
    fireEvent.click(screen.getByLabelText("显示信息"));
    // 信息应显示
    expect(screen.getByText(testMessage)).toBeInTheDocument();
});

示例二:登录组件

import * as React from "react";

function Login() {
    const [state, setState] = React.useReducer(
        (s, a) => ({ ...s, ...a }),
        { resolved: false, loading: false, error: null }
    );

    function handleSubmit(event) {
        event.preventDefault();
        const { usernameInput, passwordInput } = event.target.elements;

        setState({ loading: true, resolved: false, error: null });

        window
            .fetch("/api/login", {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify({
                    username: usernameInput.value,
                    password: passwordInput.value,
                }),
            })
            .then((r) =>
                r.json().then((data) => (r.ok ? data : Promise.reject(data)))
            )
            .then(
                (user) => {
                    setState({ loading: false, resolved: true, error: null });
                    window.localStorage.setItem("token", user.token);
                },
                (error) => {
                    setState({ loading: false, resolved: false, error: error.message });
                }
            );
    }

    return (
        <div>
            <form onSubmit={handleSubmit}>
                <div>
                    <label htmlFor="usernameInput">Username</label>
                    <input id="usernameInput" />
                </div>
                <div>
                    <label htmlFor="passwordInput">Password</label>
                    <input id="passwordInput" type="password" />
                </div>
                <button type="submit">
                    Submit{state.loading ? "..." : null}
                </button>
            </form>
            {state.error ? <div role="alert">{state.error}</div> : null}
            {state.resolved ? (
                <div role="alert">Congrats! You're signed in!</div>
            ) : null}
        </div>
    );
}

export default Login;

该组件处理用户的登录请求,根据请求结果显示不同的信息。以下是对应的测试代码:

import { rest } from "msw";
import { setupServer } from "msw/node";
import { render, screen, fireEvent } from "@testing-library/react";
import Login from "../Login";

const fakeUserRes = { token: "fake_user_token" };
const server = setupServer(
    rest.post("/api/login", (req, res, ctx) => {
        return res(ctx.json(fakeUserRes));
    })
);

// 启动服务器
beforeAll(() => server.listen());
// 关闭服务器
afterAll(() => server.close());
// 每个测试用例完成后重置服务器状态
afterEach(() => {
    server.resetHandlers();
    window.localStorage.removeItem("token");
});

test("测试请求成功", async () => {
    render(<Login />);
    fireEvent.change(screen.getByLabelText(/Username/i), {
        target: { value: "xiejie" },
    });
    fireEvent.change(screen.getByLabelText(/Password/i), {
        target: { value: "123456" },
    });
    fireEvent.click(screen.getByText("Submit"));

    expect(await screen.findByRole("alert")).toHaveTextContent(/Congrats/i);
    expect(window.localStorage.getItem("token")).toEqual(fakeUserRes.token);
});

test("测试请求失败", async () => {
    server.use(
        rest.post("/api/login", (req, res, ctx) => {
            return res(ctx.status(500), ctx.json({ message: "服务器内部出错" }));
        })
    );

    render(<Login />);
    fireEvent.change(screen.getByLabelText(/Username/i), {
        target: { value: "xiejie" },
    });
    fireEvent.change(screen.getByLabelText(/Password/i), {
        target: { value: "123456" },
    });
    fireEvent.click(screen.getByText("Submit"));

    expect(await screen.findByRole("alert")).toHaveTextContent(/服务器内部出错/i);
    expect(window.localStorage.getItem("token")).toBeNull();
});

结论

通过本文的介绍,我们了解了如何使用 Testing Library 和 Jest 对 React 组件进行单元测试。通过对组件的行为进行测试,可以确保组件在不同情况下的表现符合预期,从而提高代码的可靠性和可维护性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2233592.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

科研绘图系列:R语言组合连线图和箱线图(linechart+boxplot)

文章目录 介绍加载R包数据数据预处理画图1画图2系统信息介绍 连线图(Line Chart)是一种常用的数据可视化图表,它通过将一系列数据点用直线段连接起来来展示数据随时间或有序类别变化的趋势。以下是连线图可以表示的一些内容: 时间序列数据:展示数据随时间变化的趋势,例如…

征程 6 工具链性能分析与优化 2|模型性能优化建议

01 引言 为了应对低、中、高阶智驾场景&#xff0c;以及当前 AI 模型在工业界的应用趋势&#xff0c;地平线推出了征程 6 系列芯片。 在软硬件架构方面&#xff0c;征程 6 不仅保持了对传统 CNN 网络的高效支持能力&#xff0c;还强化了对 Transformer 类型网络的支持&#xf…

HarmonyOS第一课 07 从网络获取数据-习题

判断题 1. 在http模块中&#xff0c;多个请求可以使用同一个httpRequest对象&#xff0c;httpRequest对象可以复用&#xff1a;F 正确(True) 错误(False) 每次请求时,都需要一个新的httpReq对象 每次使用完,都需要destory对象 2. 使用on(type: headersReceive)订阅响应头事…

Kubernetes——part10-1 kubernetes日志收集方案 ELK

一、为什么收集日志 收集日志可以用于&#xff1a; 分析用户行为监控服务器状态增强系统或应用安全性等。 二、收集哪些日志 kubernetes集群节点系统日志kubernetes集群节点应用程序日志kubernetes集群中部署的应用程序日志 三、日志收集方案 3.1 日志收集技术栈ELK(ELKB…

windows MySQL报错Packet for query is too large问题解决

1、报错 Packet for query is too large (1626 > 1024). You can change this value on the server by setting the max_allowed_packet variable.出现问题的原因&#xff1a;批量插入数据量过大 MySQL根据配置文件会限制Server接受的数据包大小。有时候插入、更新或查询时…

dns服务部署

1.搭建dns服务器能够对自定义的正向或者反向域完成数据解析查询。 2.配置从DNS服务器&#xff0c;对主dns服务器进行数据备份。 第一步&#xff0c;安装bind服务以及关闭防火墙等 正反向解析 [rootlocalhost ~]# vim /var/named/named.openlab.com ------正向资源记录文件 …

ElMessageBox 内容自定义

1. ElmessageBox弹出框显示内容设置字体颜色&#xff1a; 代码内容&#xff1a; const saveToGroup (row: Customers) > {ElMessageBox.confirm(h("i",{ style: "color: #409EFF" },"未建档客户公司无法创建线索/商机/礼品申请。"),"…

学Linux的第五天

目录 命令解释器-shell-负责解析用户输入的命令 分类&#xff1a; type --查看命令是内置命令、外置命名、alias命令 注意 Linux 中的特殊符号 拓展 命令别名aliasalias 别名原命令 - 参数 常用的别名untar&#xff0c;wget,getpass,ping,speed,ipe,c 删除别名unalias…

JavaEE初阶---servlet篇(二)(smartTomcat的使用相关错误类型)

文章目录 1.servlet创建项目问题说明2.SamrtTomcat插件3.乱码问题的说明4.其他的错误类型说明4.1常见错误之4044.2常见错误之4054.3常见错误之5004.4空白页面/无法访问 1.servlet创建项目问题说明 servlet进行这个项目创建的时候&#xff0c;我们的这个web.xml里面的这个内容就…

Centos 网络接口打vlan标签

Centos 网络接口打vlan标签 本次使用给bond打vlan标签&#xff0c;其实其他普通接口也一样 Centos创建bond前需要关闭NetworkManager [root192 network-scripts]# systemctl disable NetworkManager --now Removed symlink /etc/systemd/system/multi-user.target.wants/Netwo…

使用Docker-Compose安装redis,rabbitmq,nacos,mysql,nginx,tomcat,portainer组件教程

因为开发经常会用到一些组件&#xff0c;又不想在本地启动&#xff0c;所以买了个服务器&#xff0c;然后将这些组件都安装到服务器上以便开发使用。下面就记录下使用docker-compose安装组件的教程以及一些需要注意的地方。 关于docker和docker-compose的安装在另一篇博客中有…

WPF+MVVM案例实战(二十二)- 制作一个侧边弹窗栏(CD类)

文章目录 1、案例效果1、侧边栏分类2、CD类侧边弹窗实现1、样式代码实现2、功能代码实现3 运行效果4、源代码获取1、案例效果 1、侧边栏分类 A类 :左侧弹出侧边栏B类 :右侧弹出侧边栏C类 :顶部弹出侧边栏D类 :底部弹出侧边栏2、CD类侧边弹窗实现 1、样式代码实现 在原有的…

解决 “Error: listen EACCES: permission denied 0.0.0.0:80“ 错误

前言 在开发过程中&#xff0c;我们经常会遇到各种各样的错误。其中一个常见的错误是 Error: listen EACCES: permission denied 0.0.0.0:80。这个错误通常发生在尝试启动一个开发服务器时&#xff0c;服务器试图绑定到80端口&#xff0c;但由于权限不足而失败。本文将详细介绍…

国产之光-海豚调度器的入门知识篇

目录 概念和定义 核心特性 核心组件 工作流程 环境准备 系统软件方面 硬件方面 部署方式 单机模式 伪集群模式 集群模式 基本配置 工作流定义 调度管理 定时调度 手动调度 监控告警 任务类型扩展 资源管理 权限控制 概念和定义 Apache DolphinScheduler是一…

使用Netty实现一个简单的聊天服务器

✅作者简介&#xff1a;热爱Java后端开发的一名学习者&#xff0c;大家可以跟我一起讨论各种问题喔。 &#x1f34e;个人主页&#xff1a;Hhzzy99 &#x1f34a;个人信条&#xff1a;坚持就是胜利&#xff01; &#x1f49e;当前专栏&#xff1a;Netty &#x1f96d;本文内容&a…

新世联科技:NG2-A-7在DAC空气捕集提取CO2的应用

一、DAC空气捕集提取CO2的介绍 直接空气碳捕获&#xff08;Direct Air Capture&#xff0c;简称DAC&#xff09;是一种直接从大气中提取二氧化碳的技术。 二、DAC空气捕集提取CO2的前景 从大气中提取的这种二氧化碳可以作为循环经济的一部分以各种不同方式使用。未来&#xf…

ISUP协议视频平台EasyCVR视频融合平台接入各类摄像机的方法

安防视频监控ISUP协议视频平台EasyCVR兼容性强、支持灵活拓展&#xff0c;平台可提供视频远程监控、录像、存储与回放、视频转码、视频快照、告警、云台控制、语音对讲、平台级联等视频能力。 想要将摄像机顺利接入EasyCVR平台&#xff0c;实现视频监控的集中管理和分发&#x…

(五)Spark大数据开发实战:灵活运用PySpark常用DataFrame API

目录 一、PySpark 二、数据介绍 三、PySpark大数据开发实战 1、数据文件上传HDFS 2、导入模块及数据 3、数据统计与分析 ①、计算演员参演电影数 ②、依次罗列电影番位前十的演员 ③、按照番位计算演员参演电影数 ④、求每位演员所有参演电影中的最早、最晚上映时间及…

达梦数据库宕机问题分析及处理

官方宕机原因排查 官方故障诊断排除 相关概念 达梦数据库宕机往往会产生core文件&#xff0c;解读core文件是分析宕机原因的主要手段&#xff0c;类似oracle的diag.trc或system dump转储文件&#xff0c;记录数据库线程状态、sql语句等。 首选的排查方向可以从内存溢出、磁盘…

spring ai 入门 之 结构化输出 - 把大模型llm返回的内容转换成java bean

目录 ​编辑 将AI非结构化文本转换为特定格式数据的应用场景说明 Spring AI 介绍 &#xff1a;为Java开发者打造的AI应用开发框架 Qwen 介绍 &#xff1a; 一个国内领先的开源大模型 Spring AI Alibaba框架介绍 &#xff1a; 一个国内最好的spring ai实现 使用spring ai …