当前位置: 首页 > news >正文

React学习教程,从入门到精通,React 单元测试:语法知识点及使用方法详解(30)

React 单元测试:语法知识点及使用方法详解

本文将详细介绍 React 单元测试的各个关键知识点,包括 JestReact Testing Library 的使用,并提供详细的代码示例和注释,帮助您全面掌握 React 单元测试的各个方面。


1. Jest 简介及环境搭建

Jest 是由 Facebook 开发的 JavaScript 测试框架,广泛用于 React 应用的单元测试。它具有零配置、快速运行、丰富的匹配器等特点。

1.1 安装 Jest

首先,确保您已经安装了 Node.js 和 npm。然后,在项目根目录下运行以下命令来安装 Jest:

npm install --save-dev jest

1.2 配置 package.json

package.json 中添加测试脚本:

{"scripts": {"test": "jest"}
}

1.3 初始化 Jest 配置(可选)

运行以下命令生成 jest.config.js 配置文件:

npx jest --init

根据提示选择适合的配置选项。


2. 匹配器方法(Matchers)

Jest 提供了丰富的匹配器,用于断言测试结果。以下是一些常用的匹配器及其用法。

2.1 基本匹配器

test('基本匹配器示例', () => {const a = 1;const b = 1;// 相等expect(a).toBe(b);// 不相等expect(a).not.toBe(null);// 数值比较expect(a).toBeGreaterThan(0);expect(a).toBeLessThan(2);// 浮点数比较const floatA = 0.1;const floatB = 0.2;expect(floatA + floatB).toBeCloseTo(0.3);
});

2.2 布尔值匹配器

test('布尔值匹配器示例', () => {const isActive = true;// 断言为真expect(isActive).toBeTruthy();// 断言为假expect(!isActive).toBeFalsy();
});

2.3 对象匹配器

test('对象匹配器示例', () => {const obj = { a: 1, b: 2 };// 断言对象包含特定属性expect(obj).toHaveProperty('a', 1);// 断言对象等于另一个对象expect(obj).toEqual({ a: 1, b: 2 });
});

2.4 数组匹配器

test('数组匹配器示例', () => {const arr = [1, 2, 3];// 断言数组包含特定元素expect(arr).toContain(2);// 断言数组长度expect(arr).toHaveLength(3);
});

3. 模拟函数(Mock Functions)

模拟函数用于测试函数调用情况,如调用次数、参数等。

3.1 创建模拟函数

test('模拟函数示例', () => {const mockCallback = jest.fn(x => x * 2);[1, 2, 3].forEach(mockCallback);// 断言模拟函数被调用了三次expect(mockCallback.mock.calls).toHaveLength(3);// 断言第一次调用参数为1expect(mockCallback.mock.calls[0][0]).toBe(1);// 断言返回值expect(mockCallback.mock.results[0].value).toBe(2);
});

3.2 模拟模块

假设有一个模块 math.js

// math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;

测试代码:

// math.test.js
import { add, subtract } from './math';jest.mock('./math', () => ({add: jest.fn(),subtract: jest.fn(),
}));test('模拟模块示例', () => {add.mockImplementation((a, b) => a + b);subtract.mockImplementation((a, b) => a - b);expect(add(2, 3)).toBe(5);expect(subtract(5, 2)).toBe(3);
});

4. 异步代码测试

Jest 支持对异步代码进行测试,包括 Promise 和 async/await。

4.1 使用 done 回调

test('异步测试示例 - done', (done) => {function fetchData(callback) {setTimeout(() => {callback('Hello');}, 1000);}fetchData((data) => {expect(data).toBe('Hello');done();});
});

4.2 使用 Promise

test('异步测试示例 - Promise', () => {function fetchData() {return new Promise((resolve) => {setTimeout(() => {resolve('Hello');}, 1000);});}return fetchData().then(data => {expect(data).toBe('Hello');});
});

4.3 使用 async/await

test('异步测试示例 - async/await', async () => {function fetchData() {return new Promise((resolve) => {setTimeout(() => {resolve('Hello');}, 1000);});}const data = await fetchData();expect(data).toBe('Hello');
});

5. 钩子(Hooks)

Jest 提供了 beforeEach, afterEach, beforeAll, afterAll 等钩子,用于在测试前后执行代码。

5.1 beforeEachafterEach

let counter = 0;beforeEach(() => {counter = 0;
});afterEach(() => {console.log('测试结束,counter:', counter);
});test('beforeEach 和 afterEach 示例', () => {counter += 1;expect(counter).toBe(1);
});test('另一个测试', () => {counter += 2;expect(counter).toBe(2);
});

5.2 beforeAllafterAll

let setupData;beforeAll(() => {setupData = { a: 1, b: 2 };console.log('设置数据:', setupData);
});afterAll(() => {console.log('清理数据');
});test('beforeAll 和 afterAll 示例', () => {expect(setupData.a).toBe(1);
});

6. 快照测试(Snapshot Testing)

快照测试用于确保 UI 组件在不期望的情况下不会发生变化。

6.1 创建快照

import React from 'react';
import renderer from 'react-test-renderer';
import MyComponent from './MyComponent';test('快照测试示例', () => {const tree = renderer.create(<MyComponent />).toJSON();expect(tree).toMatchSnapshot();
});

首次运行测试时,Jest 会生成一个快照文件 __snapshots__/MyComponent.test.js.snap。后续测试会与该快照进行比较。

6.2 更新快照

当组件发生变化且预期变化时,可以使用 --updateSnapshot 参数更新快照:

jest --updateSnapshot

7. DOM 测试工具

7.1 react-testing-library

react-testing-library 是一个用于测试 React 组件的库,侧重于测试组件的行为而非实现细节。

7.1.1 安装
npm install --save-dev @testing-library/react @testing-library/jest-dom
7.1.2 使用示例
// MyComponent.js
import React from 'react';const MyComponent = () => {return (<div><h1>Hello, World!</h1><button>Click Me</button></div>);
};export default MyComponent;
// MyComponent.test.js
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import MyComponent from './MyComponent';test('渲染组件并点击按钮', () => {render(<MyComponent />);// 断言标题存在const heading = screen.getByText(/hello, world!/i);expect(heading).toBeInTheDocument();// 模拟点击按钮const button = screen.getByText(/click me/i);fireEvent.click(button);// 这里可以添加更多断言,例如按钮点击后的行为
});

7.2 Enzyme

Enzyme 是由 Airbnb 开发的 React 组件测试工具,提供了浅渲染(shallow rendering)和完全渲染(full rendering)等功能。

7.2.1 安装
npm install --save-dev enzyme enzyme-adapter-react-16

注意:根据您使用的 React 版本选择合适的 Enzyme Adapter。

7.2.2 配置 Enzyme
// setupTests.js
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';configure({ adapter: new Adapter() });

package.json 中添加:

{"jest": {"setupFilesAfterEnv": ["<rootDir>/setupTests.js"]}
}
7.2.3 使用示例
// MyComponent.js
import React from 'react';const MyComponent = () => {return (<div><h1>Hello, World!</h1><button>Click Me</button></div>);
};export default MyComponent;
// MyComponent.test.js
import React from 'react';
import { shallow } from 'enzyme';
import MyComponent from './MyComponent';test('Enzyme 渲染组件并点击按钮', () => {const wrapper = shallow(<MyComponent />);// 断言标题存在const heading = wrapper.find('h1');expect(heading.text()).toBe('Hello, World!');// 模拟点击按钮const button = wrapper.find('button');button.simulate('click');// 这里可以添加更多断言,例如按钮点击后的行为
});

8. 综合案例

8.1 使用 react-testing-library 测试计数器组件

8.1.1 组件代码
// Counter.js
import React, { useState } from 'react';const Counter = () => {const [count, setCount] = useState(0);return (<div><h1>Count: {count}</h1><button onClick={() => setCount(count + 1)}>Increment</button><button onClick={() => setCount(count - 1)}>Decrement</button></div>);
};export default Counter;
8.1.2 测试代码
// Counter.test.js
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import Counter from './Counter';test('计数器组件测试', () => {render(<Counter />);// 断言初始计数为0const countElement = screen.getByText(/count:/i);expect(countElement).toHaveTextContent('Count: 0');// 模拟点击 Increment 按钮const incrementButton = screen.getByText(/increment/i);fireEvent.click(incrementButton);// 断言计数变为1expect(countElement).toHaveTextContent('Count: 1');// 模拟点击 Decrement 按钮const decrementButton = screen.getByText(/decrement/i);fireEvent.click(decrementButton);// 断言计数回到0expect(countElement).toHaveTextContent('Count: 0');
});

8.2 使用 Enzyme 测试 Redux 组件

8.2.1 Redux 配置
// store.js
import { createStore } from 'redux';const initialState = {count: 0,
};const reducer = (state = initialState, action) => {switch (action.type) {case 'INCREMENT':return { count: state.count + 1 };case 'DECREMENT':return { count: state.count - 1 };default:return state;}
};const store = createStore(reducer);export default store;
8.2.2 组件代码
// ConnectedCounter.js
import React from 'react';
import { connect } from 'react-redux';const ConnectedCounter = ({ count, dispatch }) => {return (<div><h1>Count: {count}</h1><button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button><button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button></div>);
};const mapStateToProps = (state) => ({count: state.count,
});export default connect(mapStateToProps)(ConnectedCounter);
8.2.3 测试代码
// ConnectedCounter.test.js
import React from 'react';
import { shallow } from 'enzyme';
import ConnectedCounter from './ConnectedCounter';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-redux';const middlewares = [];
const mockStore = configureStore(middlewares);test('Redux 计数器组件测试', () => {const initialState = { count: 0 };const store = mockStore(initialState);const wrapper = shallow(<Provider store={store}><ConnectedCounter /></Provider>);// 断言计数显示为0const countElement = wrapper.find('h1');expect(countElement.text()).toBe('Count: 0');// 模拟点击 Increment 按钮const incrementButton = wrapper.find('button').at(0);incrementButton.simulate('click');// 断言 Redux action 被正确分发const actions = store.getActions();expect(actions).toEqual([{ type: 'INCREMENT' }]);// 更新组件状态wrapper.update();// 断言计数显示为1expect(countElement.text()).toBe('Count: 1');
});

9. 本章小结

本文详细介绍了 React 单元测试的各个方面,包括:

  • Jest 的基础语法和匹配器方法。
  • 模拟函数 的使用。
  • 异步代码测试 的不同方法。
  • 钩子 的使用。
  • 快照测试 的概念和应用。
  • React Testing LibraryEnzyme 的使用。
  • 综合案例 展示了如何测试不同类型的 React 组件。

通过掌握这些知识点,您将能够有效地编写和运行 React 单元测试,确保应用程序的可靠性和稳定性。

http://www.dtcms.com/a/406530.html

相关文章:

  • Wouter 和 React Router的区别
  • 关于如何让 Vosk 正确识别中文音频,核心是 使用 Vosk 中文模型 + 确保中文音频格式符合要求
  • MySQL和PostgreSQL的数据库主键索引都是B+树吗?
  • VUE、jquery、React、Ant Design、element ui、bootstrap 前端框架的 功能总结,示例演示、使用场景介绍、完整对比总结
  • PDF格式转换、PDF编辑全功能解锁,功能图文教程
  • C++项目:仿muduo库高并发服务器
  • 建设网站业务不好做请问那个网站做推广好点
  • Pytest+requests进行接口自动化测试6.0(Jenkins)
  • elasticsearch安装插件
  • html5移动网站开发html5新增标签
  • 前端工程化基础知识
  • 【C#】以 BlockingCollection 为核心的多相机 YOLO 检测任务处理框架
  • (11)ASP.NET Core2.2 中的配置一(Configuration)
  • 在 C# .NETCore 中使用 MongoDB(第 1 部分):驱动程序基础知识和插入文档
  • php做图片交互网站代码网站下要加个备案号 怎么做
  • 巴中商城网站建设wordpress 图书主题
  • 一键预约上门服务:到家洗车小程序的便捷功能与场景化体验
  • 第 3 篇:让图形动起来 - WebGL 2D 变换
  • 寝室快修|基于SprinBoot+vue的贵工程寝室快修小程序(源码+数据库+文档)
  • 青秀网站建设网站海外推广平台
  • 前端基础:从0到1实现简单网页效果(二)
  • 基于LazyLLM搭建AI创意文案生成器(多场景文案自动生成)
  • 数据链路层:网络通信的基础与桥梁
  • Keepalived两个集群实验
  • vs网站开发表格大小设置网站建设要注意一些什么
  • js网站模板下载做网站和app哪个难
  • MySQL多实例管理
  • 用dpdk实现udp、tcp数据包收发,tcp协议栈相关原理
  • 3D气象数据可视化:使用前端框架实现动态天气展示
  • 学习日记19:GRAPH-BERT