news 2026/4/26 17:29:58

使用 Vitest 高效地进行组件测试

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 Vitest 高效地进行组件测试

上一篇文章探讨了如何使用 Vitest 和 React 测试库将 React Hooks 作为独立单元进行测试。在这篇文章中,我们将继续学习如何以可维护和可扩展的方式利用 React 组件进行单元测试。

先决条件

您应该设置并运行一个 React 项目。推荐的方法是使用命令 来初始化您的项目,并使用 Vite 作为其捆绑管理工具npm create vite@latest

为了进行测试,我们需要安装以下依赖项:

  • Vitest作为单元测试框架
  • JSDOM作为运行测试的 DOM 环境
  • React 测试库作为 React 测试实用程序

为此,我们运行以下命令:

npm install -D vitest jsdom @testing-library/react #OR yarn add -D vitest jsdom @testing-library/react

vitest.config.js(或vite.config.js对于 Vite 项目)中,我们添加以下test对象:

//... export default defineConfig({ test: { global: true, environment: 'jsdom', }, })

我们还在文件中添加了一个新test:unit命令package.json来运行单元测试 ,如下所示:

"scripts": { "test:unit": "vitest --root src/", }

接下来,我们将进行额外的设置以使 Vitest 断言 DOM 元素。

扩展Vitest的expect方法

Vitest 提供了用于expect断言值的基本断言方法。但是,它没有 DOM 元素的断言方法,例如toBeInTheDocument()toHaveTextContent()。对于此类方法,我们可以安装该@testing-library/jest-dom包并扩展expectVitest 中的方法以包含该包中的断言方法matchers

为此,我们将setupTest.js在项目的根目录中创建一个文件并添加以下代码:

/**setupTest.js */ import { expect } from 'vitest'; import matchers from '@testing-library/jest-dom/matchers'; expect.extend(matchers);

在 中vitest.config.js,我们可以将setupTest.js文件添加到test.setupFiles字段中:

//vitest.config.js /**... */ test: { /**... */ setupFiles: './setupTest.js', }, /**... */

通过此设置,expect()现在将拥有测试 React 组件所需的所有 DOM 断言方法。

让我们看看如何使用 Vitest 和 React 测试库来测试 React 组件。

使用模拟测试电影组件

在本节中,我们将研究一个简单的组件 -Movies它显示具有以下功能的电影列表:

  • 该组件从外部源获取电影列表。
  • 用户可以按标题搜索电影。

下面的屏幕截图显示了该组件在 UI 上的外观:

该组件的示例实现Movies如下:

export const Movies = () => { const { movies } = useMovies(); const {searchTerm, setSearchTerm, filteredItems: filteredMovies} = useSearch(movies); return ( <section> <div> <label htmlFor="search">Search</label> <input type="search" id="search" value={searchTerm} >import * as useMoviesHooks from '../hooks/useMovies'; import * as useSearchHooks from '../hooks/useSearch'; describe('Movies', () => { const useMoviesSpy = vi.spyOn(useMoviesHooks, 'useMovies'); const useSearchSpy = vi.spyOn(useSearchHooks, 'useSearch'); });

我们将使用方法模拟它们的返回值mockReturnValue,如下所示:

describe('Movies', () => { /**... */ it('should render the app', () => { const items = [{ title: 'Star Wars', release_date: '1977-05-25', director: 'George Lucas', opening_crawl: 'It is a period of civil war.' }]; useMoviesSpy.mockReturnValue({ movies: items, }); useSearchSpy.mockReturnValue({ searchTerm: '', setSearchTerm: vi.fn(), filteredItems: items }); /**... */ }); })

然后,我们将Movies使用renderfrom 的方法渲染组件@testing-library/react,并断言该组件按预期渲染电影列表,如下所示:

import { describe, it, expect, vi } from 'vitest'; import { Movies } from './Movies'; import { render } from '@testing-library/react'; describe('Movies', () => { /**... */ it('should render the the list of movies', () => { /**... */ const { getByTestId } = render(<Movies />); expect( getByTestId('movies-list').children.length ).toBe(items.length); }); })

该方法将检索属性值等于的getByTestId元素,然后我们可以断言其子元素等于模拟数组的长度。data-testid``movies-list``items

使用data-testid属性值是一种很好的做法,可以识别 DOM 元素以进行测试,并避免影响组件在生产和测试中的实现。

接下来,我们将测试搜索钩子在Movies.

测试搜索输入的功能

我们首先仅模拟useMovies返回一组电影的钩子,如下所示:

it('should change the filtered items when the search term changes', () => { const items = [ { title: 'Star Wars' }, { title: 'Star Trek' }, { title: 'Starship Troopers' } ]; useMoviesSpy.mockReturnValue({ movies: items, isLoading: false, error: null }); });

我们渲染Movies组件并使用getByTestId以下方法检索搜索输入字段data-testid

it('should change the filtered items when the search term changes', () => { /**... */ const { getByTestId } = render(<Movies />); const searchInput = getByTestId('search-input-field'); });

为了测试 UI 中的搜索功能Movies,我们将使用以下两种方法@testing-library/react

  • fireEvent.change()- 模拟change搜索输入字段上的用户事件。
  • act()- 环绕用户事件模拟的执行,并确保在继续对 UI 上显示的项目数量进行断言之前,所有更新都应用于 DOM。
import { fireEvent, render, act } from '@testing-library/react'; it('should change the filtered items when the search term changes', () => { /**... */ act(() => { fireEvent.change(searchInput, { target: { value: 'Wars' } }); }) expect( getByTestId('movies-list').children.length ).toBe(1); });

通过此,我们测试了用户与搜索输入字段的交互以及组件对用户输入的响应。

但是,如果您在上一个测试之后运行该测试,则该测试将失败,因为 的最后一个模拟值useSearch仍然有效。我们必须在每次测试后清理并恢复原始实现,以确保每个测试用例的模拟值都是隔离的。我们将在下一节中这样做。

每次测试后clear模拟

为了清除我们监视的每个钩子的任何模拟值,我们将触发mockClear()如下:

afterEach(() => { useMoviesSpy.mockClear(); useSearchSpy.mockClear(); });

使用此代码,每次测试运行后,Vitest 将清除任何现有的模拟值或间谍挂钩的实现,为下一次测试运行做好准备。或者,我们可以使用它mockRestore()来恢复非模拟实现。

接下来,我们将应用类似的方法,为每个测试运行(但所有测试套件)清理 DOM。

每次测试后clear DOM

在 中setupTest.js,我们可以在每次测试后运行cleanupfrom 的方法@testing-library/react来清理 DOM,使用afterEachVitest 中的方法,如下所示:

/**setupTest.js */ import { expect, afterEach } from 'vitest'; import { cleanup } from '@testing-library/react'; /**... */ afterEach(() => { cleanup(); });

通过这样做,我们可以确保 DOM 在每次测试运行之前都是干净的,并将其应用于所有测试套件。

概括

本文向我们展示了如何使用 React 测试库和 Vitest 包以及正确的模拟方法和适当的测试方法来测试 React 组件。

我们可以将示例测试的测试扩展到涵盖更多场景,例如在加载电影时测试加载状态或错误状态,或者为搜索输入添加更多过滤选项。通过正确的组件和钩子结构,我们可以以有组织且可扩展的方式创建我们的测试系统。

最后:下方这份完整的软件测试 视频教程已经整理上传完成,需要的朋友们可以自行领取【保证100%免费】

​​​件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/25 6:37:20

Phi-3.5-Mini-Instruct开源大模型部署:无需Docker、纯Python快速启动

Phi-3.5-Mini-Instruct开源大模型部署&#xff1a;无需Docker、纯Python快速启动 1. 项目简介 Phi-3.5-Mini-Instruct是微软推出的轻量级大语言模型&#xff0c;专为本地部署优化设计。这个工具完美适配了Phi-3.5模型的官方架构&#xff0c;无需复杂配置即可一键加载本地模型…

作者头像 李华
网站建设 2026/4/25 6:34:45

Windows Server 2019上搞定Connectify Dispatch网卡聚合,保姆级避坑指南

Windows Server 2019网卡聚合实战&#xff1a;Connectify Dispatch配置全解析与深度优化 在当今数据中心和服务器环境中&#xff0c;网络带宽已成为关键性能瓶颈。对于运行Windows Server 2019的管理员而言&#xff0c;如何充分利用多网卡硬件资源实现带宽叠加和智能分流&#…

作者头像 李华
网站建设 2026/4/25 6:33:22

蓝桥杯I2C实战:EEPROM数据持久化与PCF8591信号转换

1. I2C总线与智能环境监测模块设计 在蓝桥杯单片机开发中&#xff0c;I2C总线因其简单的两线制结构&#xff08;SDA数据线和SCL时钟线&#xff09;和多设备共享特性&#xff0c;成为连接多个外设的首选方案。我们设计的智能环境监测模块需要同时处理数据存储和信号转换两大核心…

作者头像 李华
网站建设 2026/4/26 11:32:12

AI赋能新药研发!化合物虚拟筛选春日特惠3天极速交付

在新药研发与生命科学科研领域&#xff0c;靶点验证、先导化合物发现始终是决定项目进度的核心环节。传统筛选模式普遍面临化合物库资源有限、筛选周期冗长、研发成本高企的痛点&#xff0c;更难在短时间内完成海量分子的精准对接与潜力评估&#xff0c;成为无数课题组与研发团…

作者头像 李华