当前位置:   article > 正文

如何使用React+jest开展单元测试

react+jest

2024软件测试面试刷题,这个小程序(永久刷题),靠它快速找到工作了!(刷题APP的天花板)_软件测试刷题小程序-CSDN博客文章浏览阅读3.3k次,点赞86次,收藏15次。你知不知道有这么一个软件测试面试的刷题小程序。里面包含了面试常问的软件测试基础题,web自动化测试、app自动化测试、接口测试、性能测试、自动化测试、安全测试及一些常问到的人力资源题目。最主要的是他还收集了像阿里、华为这样的大厂面试真题,还有互动交流板块……_软件测试刷题小程序​编辑https://blog.csdn.net/AI_Green/article/details/134931243?spm=1001.2014.3001.5502https://blog.csdn.net/AI_Green/article/details/134931243?spm=1001.2014.3001.5502https://blog.csdn.net/AI_Green/article/details/134931243?spm=1001.2014.3001.5502​编辑https://blog.csdn.net/AI_Green/article/details/134931243?spm=1001.2014.3001.5502icon-default.png?t=N7T8https://blog.csdn.net/AI_Green/article/details/134931243?spm=1001.2014.3001.5502

具体框架下(cra,umi,antd-pro等)环境的配置略有不同,后续单元测试的写法都是雷同的;环境的配置在面试时不是重点,如何写单测本身才是!

本例以Vite+React+TS为例进行说明。

1. 安装依赖

首先,你需要安装Jest本身以及一些与React和TypeScript相关的依赖。打开终端,切换到你的项目目录,然后运行以下命令:

npm install --save-dev jest @types/jest ts-jest @testing-library/react @testing-library/jest-dom

这将安装Jest、Jest的TypeScript支持、React的测试库以及必要的类型定义。

  • jest: Jest是一个广泛使用的JavaScript测试框架,它允许你编写和运行测试,包括单元测试、集成测试和端到端测试。Jest提供了测试运行器、断言库、和模拟支持,使得测试JavaScript代码变得简单快捷。
  • @types/jest: 这是Jest的类型定义包,用于TypeScript。由于Jest本身是用JavaScript编写的,@types/jest提供了所有Jest函数和对象的TypeScript类型定义。这使得在使用TypeScript编写测试时,你可以获得类型检查和代码自动完成等功能。
  • ts-jest: ts-jest是一个Jest插件,用于处理TypeScript代码。它允许Jest直接运行TypeScript文件,而不需要先将它们转换为JavaScript。ts-jest提供了TypeScript的编译和配置支持,确保测试代码能够正确地处理TypeScript的特性。
  • @testing-library/react: 这是React Testing Library的核心包,它提供了一套用于测试React组件的实用函数。React Testing Library的目标是模拟用户在使用应用时的行为,而不是测试组件的内部状态,从而鼓励更好的测试实践。它提供了查询DOM元素、触发事件等功能,使得测试React组件变得更加简单和直观。
  • @testing-library/jest-dom: 这是一个自定义的Jest匹配器集合,用于改善与DOM元素交互的测试。@testing-library/jest-dom提供了一系列额外的DOM元素断言,如.toBeVisible()、.toHaveClass()等,这些断言使得在使用Jest测试DOM元素时更加方便和语义化。

2. 配置Jest

接下来,你需要创建一个Jest配置文件。你可以在项目根目录下创建一个jest.config.js文件,并添加以下配置:

  1. module.exports = {
  2. // 使用ts-jest预设,这个预设包含了处理TypeScript文件所需的所有配置
  3. preset: 'ts-jest',
  4. // 设置测试环境为jsdom,jsdom模拟了一个浏览器环境,允许在Node环境下运行浏览器特定的API
  5. testEnvironment: 'jsdom',
  6. // 在每个测试文件运行之后,立即执行指定的脚本文件,这里是项目根目录下的src/setupTests.ts
  7. // 通常用于全局的测试设置,比如配置enzyme或jest-dom等
  8. setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'],
  9. // 配置模块名称映射,用于将导入语句中的别名映射到实际的文件路径
  10. moduleNameMapper: {
  11. // 将@components别名映射到src/components目录
  12. // 这样在测试中可以使用@components/xxx来引入组件
  13. // 在tsconfig.json中也需要进行对称的配置
  14. '^@components/(.*)$': '<rootDir>/src/components/$1',
  15. },
  16. // 配置文件转换规则,告诉Jest如何处理项目中的不同类型的文件
  17. transform: {
  18. // 使用ts-jest处理.ts和.tsx文件
  19. // 这允许Jest理解TypeScript语法并将其转换为JavaScript
  20. '^.+\\.(ts|tsx)$': 'ts-jest',
  21. // 使用babel-jest处理.js和.jsx文件
  22. // 这允许Jest通过Babel转换这些文件,支持ES6语法和React JSX
  23. '^.+\\.(js|jsx)$': 'babel-jest',
  24. },
  25. // 指定Jest在转换过程中应该忽略的文件模式
  26. // 这里配置为忽略node_modules目录下的所有文件,这些文件通常不需要转换
  27. transformIgnorePatterns: ['<rootDir>/node_modules/'],
  28. };

这个配置做了几件事情:

  • 使用ts-jest预设来处理TypeScript文件。

  • 设置测试环境为jsdom,这对于模拟浏览器环境的React测试很有用。

  • 指定了一个setupFilesAfterEnv配置,这允许你在每次测试之前自动加载一些配置或全局mocks。

  • moduleNameMapper用于解析模块别名,这在你的项目中使用了如Webpack别名时非常有用。

  • transform配置告诉Jest如何处理.ts、.tsx、.js和.jsx文件。

3. 设置测试启动文件

在src目录下创建一个setupTests.ts文件,用于配置或添加一些在测试之前需要运行的代码。例如,你可以在这里导入@testing-library/jest-dom以扩展Jest的断言库:

import '@testing-library/jest-dom';

4. 编写测试

现在可以开始编写测试了。

测试普通函数

此处我们写一个缓存执行结果的闭包

src/utils/functionalUtil.ts

  1. export function cacheIt(fn: Function) {
  2. const cache = new Map<string, any>();
  3. return function (...args: any[]) {
  4. const key = JSON.stringify(args);
  5. if (!cache.has(key)) {
  6. const result = fn.apply(null, args);
  7. cache.set(key, result);
  8. return result;
  9. }
  10. return cache.get(key);
  11. }
  12. }

使用jest断言语法编写测试脚本

tests/utils/functionalUtil.test.ts

  1. // 从项目的utils/functionalUtil模块中导入cacheIt函数
  2. import { cacheIt } from '@/utils/functionalUtil';
  3. // 使用describe定义一组测试用例,这组测试用例的目的是测试cacheIt函数
  4. describe('cacheIt', () => {
  5. // 定义一个测试用例,测试cacheIt是否能正确缓存函数结果
  6. it('should cache and return the result for the same arguments', () => {
  7. // 使用jest.fn创建一个模拟函数add,模拟一个加法操作
  8. const add = jest.fn((a: number, b: number) => a + b);
  9. // 使用cacheIt函数对add函数进行缓存处理
  10. const cachedAdd = cacheIt(add);
  11. // 调用cachedAdd函数两次,传入相同的参数(2, 3),并期望返回值为5
  12. expect(cachedAdd(2, 3)).toBe(5);
  13. expect(cachedAdd(2, 3)).toBe(5);
  14. // 验证add函数只被实际调用了一次,因为第二次调用时结果应该是从缓存中获取的
  15. expect(add).toHaveBeenCalledTimes(1);
  16. // 再次调用cachedAdd函数,但这次传入不同的参数(3, 4),并期望返回值为7
  17. expect(cachedAdd(3, 4)).toBe(7);
  18. // 验证add函数此时被调用了两次,因为传入了新的参数组合
  19. expect(add).toHaveBeenCalledTimes(2);
  20. });
  21. // 定义另一个测试用例,测试对于相同的参数是否总是返回缓存的结果
  22. it('should return cached result for the same arguments called multiple times', () => {
  23. // 使用jest.fn创建一个模拟函数multiply,模拟一个乘法操作
  24. const multiply = jest.fn((a: number, b: number) => a * b);
  25. // 使用cacheIt函数对multiply函数进行缓存处理
  26. const cachedMultiply = cacheIt(multiply);
  27. // 分别两次调用cachedMultiply函数,传入相同的参数(4, 5)
  28. const firstCallResult = cachedMultiply(4, 5);
  29. const secondCallResult = cachedMultiply(4, 5);
  30. // 验证两次调用的返回值都为20
  31. expect(firstCallResult).toBe(20);
  32. expect(secondCallResult).toBe(20);
  33. // 验证multiply函数只被实际调用了一次,因为第二次调用时结果应该是从缓存中获取的
  34. expect(multiply).toHaveBeenCalledTimes(1);
  35. });
  36. });

这组测试用例通过模拟函数和cacheIt函数的组合使用,验证了cacheIt能够正确地缓存函数调用结果,并在相同参数的后续调用中返回缓存的结果,从而减少实际函数调用的次数。

5. 运行测试

最后,你需要在package.json中添加一个脚本来运行测试:

  1. {
  2.   "scripts": {
  3.     "test""jest"
  4.   }
  5. }

现在,你可以通过运行以下命令来执行你的测试:

npm test

这些步骤应该帮助你在使用Vite、React和TypeScript的项目中集成Jest进行单元测试。记得根据你的项目需求调整配置和测试。

image.png

6. 测试网络请求

编写一个模拟异步返回数据的API函数

src/apis/modelOne.ts

  1. export async function fetchData(url: string): Promise<any> {
  2. try {
  3. const response = await fetch(url);
  4. if (!response.ok) {
  5. throw new Error('Network response was not ok');
  6. }
  7. const data = await response.json();
  8. return data;
  9. } catch (error) {
  10. throw new Error(`Fetching data failed: ${error}`);
  11. }
  12. }

编写测试脚本

tests/apis/modelOne.test.ts

  1. import { fetchData } from '@/apis/modelOne'; // 从modelOne模块导入fetchData函数
  2. // 使用jest来模拟全局的fetch
  3. global.fetch = jest.fn(() =>
  4. Promise.resolve({
  5. ok: true, // 模拟fetch请求成功返回
  6. json: () => Promise.resolve({ message: 'Success' }), // 模拟返回的JSON数据
  7. })
  8. ) as jest.Mock; // 将模拟的fetch函数强制类型转换为jest.Mock类型
  9. beforeEach(() => {
  10. // 在每个测试用例运行之前清除模拟的调用和实例
  11. (fetch as jest.Mock).mockClear(); // 清除fetch模拟函数的调用记录
  12. });
  13. test('fetchData returns data on successful fetch', async () => {
  14. const data = await fetchData('https://example.com/data'); // 调用fetchData函数
  15. expect(fetch).toHaveBeenCalledTimes(1); // 验证fetch是否被调用了一次
  16. expect(fetch).toHaveBeenCalledWith('https://example.com/data'); // 验证fetch是否用正确的URL被调用
  17. expect(data).toEqual({ message: 'Success' }); // 验证fetchData函数的返回值是否符合预期
  18. });
  19. test('fetchData throws an error when fetch fails', async () => {
  20. (fetch as jest.Mock).mockRejectedValue(new Error('Failed to fetch')); // 模拟fetch请求失败
  21. await expect(fetchData('https://example.com/data')).rejects.toThrow('Fetching data failed: Error: Failed to fetch'); // 验证当fetch失败时,fetchData函数是否按预期抛出错误
  22. });

这个测试文件主要做了两件事:1. 使用jest.fn()模拟全局的fetch函数,以便在不发出真实网络请求的情况下测试fetchData函数的行为。模拟的fetch函数可以根据需要返回成功或失败的响应。2. 定义了两个测试用例:

  • 第一个测试用例验证当fetch成功时,fetchData函数是否正确返回数据。

  • 第二个测试用例验证当fetch失败时,fetchData函数是否抛出了预期的错误。

7. 测试同步组件

src/components/Hello.tsx

  1. export default function Hello({ name }: { name: string }) {
  2. return (
  3. <h1>Hello, {name}! </h1>
  4. )
  5. }

tests/components/Hello.test.tsx

  1. // 从@testing-library/react库中导入render和screen工具
  2. import { render, screen } from '@testing-library/react';
  3. // 从项目的components目录中导入Hello组件
  4. import Hello from '@components/Hello';
  5. // 定义一个测试用例,测试名称为'renders hello message'
  6. test('renders hello message', () => {
  7. // 使用render函数渲染Hello组件,并传入props,这里传入的name为"world"
  8. render(<Hello name="world" />);
  9. // 使用screen.getByText查询函数来查找页面上的文本内容
  10. // 这里使用正则表达式/i来忽略大小写,匹配文本"hello, world!"
  11. const helloElement = screen.getByText(/hello, world!/i);
  12. // 使用expect函数和toBeInTheDocument匹配器来断言
  13. // 检查helloElement是否成功渲染在了文档中
  14. expect(helloElement).toBeInTheDocument();
  15. });

这个测试用例的目的是验证Hello组件是否能够根据传入的name prop正确渲染出"hello, world!"这个消息。通过@testing-library/react提供的render函数来渲染组件,并使用screen.getByText来查询渲染结果中是否包含了期望的文本内容。最后,使用expect和toBeInTheDocument来断言查询到的元素确实存在于文档中,从而验证组件的渲染行为。

8. 测试异步数据组件

src/components/MessageFetcher.tsx

  1. import /* React, */ { useState } from 'react';
  2. export const MessageFetcher = () => {
  3. const [message, setMessage] = useState('');
  4. const fetchMessage = async () => {
  5. try {
  6. const response = await fetch('https://api.example.com/message');
  7. const data = await response.json();
  8. setMessage(data.message);
  9. } catch (error) {
  10. console.error('Fetching message failed:', error);
  11. setMessage('Error fetching message');
  12. }
  13. };
  14. return (
  15. <div>
  16. <button onClick={fetchMessage}>Fetch Message</button>
  17. {message && <p>{message}</p>}
  18. </div>
  19. );
  20. };

tests/components/MessageFetcher.test.tsx

  1. // 从@testing-library/react库中导入render, screen, fireEvent, 和 waitFor工具
  2. import { render, screen, fireEvent, waitFor } from '@testing-library/react';
  3. // 导入@testing-library/jest-dom以获得额外的jest断言方法
  4. import '@testing-library/jest-dom';
  5. // 从项目的components目录中导入MessageFetcher组件
  6. import { MessageFetcher } from '@components/MessageFetcher';
  7. // 使用jest.fn()模拟全局的fetch函数
  8. global.fetch = jest.fn(() =>
  9. Promise.resolve({
  10. json: () => Promise.resolve({ message: 'Hello from the API' }), // 模拟fetch请求成功,并返回一个对象,该对象包含一个json方法,json方法返回一个解析为包含特定消息的对象的Promise
  11. })
  12. ) as jest.Mock; // 将模拟的fetch函数强制类型转换为jest.Mock类型
  13. // 使用describe函数定义一组相关的测试
  14. describe('MessageFetcher', () => {
  15. beforeEach(() => {
  16. // 在每个测试用例运行之前,使用mockClear方法清除fetch模拟函数的调用记录和实例
  17. (fetch as jest.Mock).mockClear();
  18. });
  19. it('fetches and displays the message', async () => {
  20. // 使用render函数渲染MessageFetcher组件
  21. render(<MessageFetcher />);
  22. // 使用fireEvent.click模拟用户点击操作,触发获取消息的按钮
  23. fireEvent.click(screen.getByText('Fetch Message'));
  24. // 使用waitFor异步等待,直到期望的断言通过
  25. await waitFor(
  26. // 使用expect函数和toBeInTheDocument断言方法来检查页面上是否成功显示了API返回的消息
  27. () => expect(screen.getByText('Hello from the API')).toBeInTheDocument()
  28. );
  29. // 检查fetch是否被准确地调用了一次
  30. expect(fetch).toHaveBeenCalledTimes(1);
  31. });
  32. });

这个测试文件主要做了以下几件事:

  • 1. 使用jest.fn()模拟全局的fetch函数,以便在不发出真实网络请求的情况下测试组件的行为。模拟的fetch函数被配置为返回一个成功的响应,其中包含一个消息。
  • 2. 使用describe和it定义测试套件和测试用例,beforeEach用于设置每个测试用例之前的初始条件,这里是清除fetch模拟的调用记录。
  • 3. 在测试用例中,首先渲染MessageFetcher组件,然后模拟用户点击操作以触发消息的获取,最后验证是否成功获取并显示了消息,以及fetch函数是否被正确调用。

批量执行效果

行动吧,在路上总比一直观望的要好,未来的你肯定会感谢现在拼搏的自己!如果想学习提升找不到资料,没人答疑解惑时,请及时加入群: 759968159,里面有各种测试开发资料和技术可以一起交流哦。

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

​​​软件测试面试文档

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

在这里插入图片描述

在这里插入图片描述

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号