文章目录
安装及配置
- 安装相关库: 首先,使用npm或yarn安装所需的库。
npminstall --save-dev jest enzyme enzyme-adapter-react-16 enzyme-to-json
- 配置Jest: 在项目根目录下创建一个
jest.config.js
文件,并配置Jest。
// jest.config.js
module.exports ={setupFilesAfterEnv:['<rootDir>/src/setupTests.js'],testEnvironment:'jsdom',moduleNameMapper:{'\\.(css|less)$':'identity-obj-proxy',},};
在上述配置中,设置了
setupFilesAfterEnv
为
src/setupTests.js
,指定了测试环境为
jsdom
,并设置了模块名称映射以处理CSS和LESS文件。
- 创建
setupTests.js
文件: 在项目的src
目录下创建一个setupTests.js
文件,并进行Enzyme的初始化和配置。
// src/setupTests.jsimport Enzyme from'enzyme';import Adapter from'enzyme-adapter-react-16';
Enzyme.configure({adapter:newAdapter()});
在上述代码中,导入Enzyme和Enzyme适配器,并使用适配器初始化Enzyme。
- 编写测试用例: 在测试目录中创建一个与要测试的组件对应的测试文件,并编写测试用例。
import React from'react';import{ mount }from'enzyme';import YourComponent from'../YourComponent';describe('YourComponent',()=>{it('renders correctly',()=>{const component =mount(<YourComponent />);expect(component.find('.your-class').text()).toBe('Hello World');});});
在上述代码中,使用
mount
方法来渲染组件,并使用Enzyme提供的API来查找组件中的元素,并进行断言验证。
- 运行测试: 在终端中运行测试命令,以执行测试用例。
npm run test
Jest将自动查找并执行项目中以
.test.js
或
.spec.js
结尾的文件中的测试用例。
enzyme渲染
Enzyme提供了三种常用的渲染方式,分别是
shallow
、
mount
和
render
。这些渲染方式在测试组件时具有不同的特点和用途。
shallow
渲染:shallow
渲染是Enzyme的浅层渲染方式,它仅渲染被测试组件本身,而不渲染其子组件。
import{ shallow }from'enzyme';import YourComponent from'../YourComponent';test('测试组件渲染',()=>{const wrapper =shallow(<YourComponent />);// 对被测试组件进行断言和验证});
使用
shallow
渲染时,被测试组件的子组件将被替换为占位符,并且无法直接访问子组件。这使得测试更加轻量且快速,适用于对被测试组件本身的行为进行测试。
mount
渲染:mount
渲染是Enzyme的全渲染方式,它会将被测试组件及其子组件完全渲染到真实的DOM中。这使得可以模拟实际的组件嵌套和交互,并对其进行全面的测试。
import{ mount }from'enzyme';import YourComponent from'../YourComponent';test('测试组件渲染',()=>{const wrapper =mount(<YourComponent />);// 对被测试组件及其子组件进行断言和验证});
使用
mount
渲染时,被测试组件及其子组件将在真实的DOM中进行渲染和交互。这使得可以测试组件的交互、生命周期方法和子组件的行为。
render
渲染:render
渲染是Enzyme的静态渲染方式,它将组件渲染为静态的HTML字符串,不涉及真实的DOM操作。这使得可以对组件的输出进行快照测试,以验证其渲染结果是否符合预期。
import{ render }from'enzyme';import YourComponent from'../YourComponent';test('测试组件渲染',()=>{const wrapper =render(<YourComponent />);// 对组件输出的HTML进行断言和验证});
使用
render
渲染时,被测试组件及其子组件将以静态HTML字符串的形式输出,可以方便地进行快照测试。
下面的例子中使用到的渲染方式只是举例,结合实际情况使用mount、shalldow或者render都是可以的。
测试技巧
一、常见测试
- 示例:测试组件渲染和属性传递 假设有一个简单的React组件
Button
,接收一个label
属性,并在按钮上显示该标签。可以编写一个测试用例来验证组件是否正确渲染和传递属性。
import React from'react';import{ shallow }from'enzyme';import Button from'../Button';describe('Button',()=>{it('renders correctly with label prop',()=>{const label ='Click me';const wrapper =shallow(<Button label={label}/>);expect(wrapper.text()).toBe(label);});});
在上述例子中,使用
shallow
渲染方法来渲染
Button
组件,并使用
expect
和
toBe
断言验证按钮上的文本是否与传递的标签相匹配。
- 示例:模拟事件处理 假设有一个表单组件
LoginForm
,其中包含一个提交按钮。可以编写一个测试用例来模拟按钮点击事件,并验证相应的事件处理函数是否被调用。
import React from'react';import{ mount }from'enzyme';import LoginForm from'../LoginForm';describe('LoginForm',()=>{it('calls onSubmit handler on button click',()=>{const onSubmit = jest.fn();const wrapper =mount(<LoginForm onSubmit={onSubmit}/>);
wrapper.find('button').simulate('click');expect(onSubmit).toHaveBeenCalledTimes(1);});});
在上述例子中,使用
mount
渲染方法来渲染
LoginForm
组件,并使用
simulate
方法模拟按钮的点击事件。然后,使用
expect
和
toHaveBeenCalledTimes
断言验证
onSubmit
函数是否被调用了一次。
- 示例:异步操作的处理 如果组件中包含异步操作,可以使用
async/await
或done
回调函数来处理异步测试。
import React from'react';import{ mount }from'enzyme';import AsyncComponent from'../AsyncComponent';describe('AsyncComponent',()=>{it('renders data correctly after fetching',async()=>{constfetchData=()=>{returnnewPromise((resolve)=>{setTimeout(()=>{resolve('Data');},1000);});};const wrapper =mount(<AsyncComponent fetchData={fetchData}/>);expect(wrapper.text()).toBe('Loading...');await Promise.resolve();// 等待异步操作完成
wrapper.update();expect(wrapper.text()).toBe('Data');});});
在上述例子中,使用
await
关键字等待异步操作的完成,并使用
wrapper.update()
手动更新组件,以便正确渲染异步操作后的数据。
二、触发ant design组件
使用Ant Design的组件时,可以通过模拟事件或直接调用组件的方法来触发各种操作。下面是一些常见组件的触发方法示例:
- Input 输入框:
import{ mount }from'enzyme';import{ Input }from'antd';test('触发Input输入',()=>{const wrapper =mount(<Input />);// 模拟输入
wrapper.find('input').simulate('change',{target:{value:'Hello'}});// 模拟回车
wrapper.find('input').simulate('keydown',{keyCode:13,which:13}});// 模拟失去焦点test('模拟输入框失去焦点',()=>{const onBlur = jest.fn();// 失去焦点时调用的函数const wrapper =mount(<YourComponent onBlur={onBlur}/>);// 找到输入框元素,并触发失去焦点事件
wrapper.find('input').simulate('blur');wrapper(onBlur).toHaveBeenCalled();});
在上述例子中,使用
simulate
方法模拟输入框的
change
事件,并传递了一个模拟的
event
对象,其中
target.value
表示输入的值。
- Select 选择器:
import{ mount }from'enzyme';import{ Select }from'antd';test('直接调用Select方法更新选项',()=>{const wrapper =mount(<Select><Select.Option value="option1">Option 1</Select.Option><Select.Option value="option2">Option 2</Select.Option></Select>);const selectInstance = wrapper.find('Select').instance();// 更新选项
selectInstance.updateOptions([{value:'option3',label:'Option 3'},{value:'option4',label:'Option 4'},]);});
在上述例子中,使用 simulate 方法模拟点击下拉菜单按钮的 mousedown 事件,然后模拟点击第一个选项的 click 事件来选择该选项。
- Checkbox 复选框:
import { mount } from 'enzyme';
import { Checkbox } from 'antd';
test('触发Checkbox选择', () => {
const wrapper = mount(<Checkbox>Checkbox</Checkbox>);
wrapper.find('input').simulate('change', { target: { checked: true } });
});
在上述例子中,使用
simulate
方法模拟复选框的
change
事件,并传递了一个模拟的
event
对象,其中
target.checked
表示复选框的选中状态。
- Dropdown 下拉菜单:
import{ mount }from'enzyme';import{ Dropdown, Menu }from'antd';test('触发Dropdown菜单',()=>{const menu =(<Menu><Menu.Item key="1">Option 1</Menu.Item><Menu.Item key="2">Option 2</Menu.Item></Menu>);const wrapper =mount(<Dropdown overlay={menu}><a href="#">Dropdown</a></Dropdown>);
wrapper.find('a').simulate('click');});
在上述例子中,使用
simulate
方法模拟下拉菜单的触发事件,例如
click
事件。
- 模拟文件上传 在React中,由于安全性的限制,不能直接模拟文件上传的完整流程。但是,可以通过模拟触发
change
事件来模拟用户选择文件的行为。这样可以触发相应的文件上传逻辑。
以下是一个示例代码,演示了如何使用
simulate
方法来模拟文件上传:
import{ mount }from'enzyme';test('模拟文件上传',()=>{const onFileUpload = jest.fn();// 模拟文件上传时调用的函数const wrapper=mount(<YourComponent onFileUpload={onFileUpload}/>);// 创建一个模拟的File对象const file =newFile(['文件内容'],'filename.txt',{type:'text/plain'});// 创建一个模拟的事件对象,设置target.files为模拟的File对象const event ={target:{files:[file]},};// 找到文件上传的input元素,并触发change事件
wrapper.find('input[type="file"]').simulate('change', event);expect(onFileUpload).toHaveBeenCalledWith(file);});
在上述代码中,创建了一个模拟的
File
对象,并将其作为事件对象的
target.files
属性。然后,使用
simulate
方法触发了一个
change
事件,模拟文件选择的操作。最后,使用
expect
断言来验证回调函数
onFileUpload
是否被调用,并传递了模拟的文件对象作为参数。
这些是一些常见Ant Design组件的触发方法示例。根据具体的组件和需求,可以使用适当的方法来模拟事件触发或直接调用组件的方法。
三、使用redux组件
当编写涉及Redux的组件的单元测试时,可以采取以下措施以确保测试不会报错:
- 模拟Redux Store: 在测试中,可以创建一个模拟的Redux Store,并将其传递给组件作为属性。可以使用类似于
redux-mock-store
的库来创建模拟的Store。
import React from'react';import{ shallow }from'enzyme';import configureMockStore from'redux-mock-store';import YourComponent from'../YourComponent';const mockStore =configureMockStore();const initialState ={// 初始状态};const store =mockStore(initialState);describe('YourComponent',()=>{it('renders without errors',()=>{const wrapper =shallow(<YourComponent store={store}/>);expect(wrapper).toBeTruthy();});});
在上述例子中,使用
redux-mock-store
库创建了一个模拟的Store,并将其传递给
YourComponent
作为属性。
- 使用Provider组件: 在测试中,使用Redux的Provider组件将模拟的Store包装在组件的外部。这样可以确保组件在测试中能够访问到Redux的状态和行为。
import React from'react';import{ shallow }from'enzyme';import{ Provider }from'react-redux';import configureMockStore from'redux-mock-store';import YourComponent from'../YourComponent';const mockStore =configureMockStore();const initialState ={// 初始状态};const store =mockStore(initialState);describe('YourComponent',()=>{it('renders without errors',()=>{const wrapper =shallow(<Provider store={store}><YourComponent /></Provider>);expect(wrapper).toBeTruthy();});});
在上述例子中,使用Redux的Provider组件将模拟的Store包装在
YourComponent
的外部,以确保组件在测试中能够正确连接到Redux。
- 测试Redux相关的行为: 在编写单元测试时,除了测试组件的渲染和行为外,还可以编写专门测试Redux相关行为的测试用例。例如,测试Redux的action创建函数、reducer函数和异步操作的处理等。
四、使用路由的组件
当编写涉及React Router的组件的单元测试时,可以采取以下措施来处理受到影响的组件:
- 使用MemoryRouter: 在测试中,使用
MemoryRouter
来包装被测试的组件。MemoryRouter
是React Router提供的一个用于测试的特殊路由器组件,它在内存中维护路由状态,不会对浏览器URL进行实际更改。
import React from'react';import{ shallow }from'enzyme';import{ MemoryRouter }from'react-router-dom';import YourComponent from'../YourComponent';describe('YourComponent',()=>{it('renders without errors',()=>{const wrapper =shallow(<MemoryRouter><YourComponent /></MemoryRouter>);expect(wrapper).toBeTruthy();});});
在上述例子中,使用
MemoryRouter
将被测试的组件包装在内部,以便在测试中模拟路由。
- 使用StaticRouter: 如果组件使用了Server-Side Rendering(SSR),可以使用
StaticRouter
来模拟服务器端渲染的路由。
import React from'react';import{ shallow }from'enzyme';import{ StaticRouter }from'react-router-dom';import YourComponent from'../YourComponent';describe('YourComponent',()=>{it('renders without errors',()=>{const context ={};const wrapper =shallow(<StaticRouter location="/your-path" context={context}><YourComponent /></StaticRouter>);expect(wrapper).toBeTruthy();});});
在上述例子中,使用
StaticRouter
来模拟服务器端渲染的路由,并通过
location
属性设置当前的路径。
- 使用
BrowserRouter
和Router
: 如果组件使用了BrowserRouter
或自定义的Router
组件,可以在测试中直接使用这些组件,并通过history
属性模拟路由历史记录。
import React from'react';import{ shallow }from'enzyme';import{ BrowserRouter, Router }from'react-router-dom';import YourComponent from'../YourComponent';import{ createMemoryHistory }from'history';describe('YourComponent',()=>{it('renders without errors',()=>{const history =createMemoryHistory();const wrapper =shallow(<Router history={history}><YourComponent /></Router>);expect(wrapper).toBeTruthy();});});
在上述例子中,使用
createMemoryHistory
创建一个内存中的
history
对象,并将其通过
Router
组件传递给被测试的组件。
五、mock接口网络请求
- mock 接口请求
// @/servise/XXX 是我自己封装的功能请求的相对地址// 方法一
jest.mock('@/servise/XXX',()=>({getData: Promise.resove({res:{}})));// 方法二import service from'@/service/XXX';
service.getData=()=>Promise.resolve({res:{}})
2.自定义模拟的请求和返回数据
import YourComponent from'../YourComponent';import axios from'axios';
jest.mock('axios');describe('YourComponent',()=>{it('should handle API request',async()=>{// 自定义模拟的请求和返回数据const mockResponseData ={name:'John Doe'};
axios.get.mockResolvedValue({data: mockResponseData });// 执行组件中的接口请求const component =newYourComponent();await component.fetchData();// 验证结果expect(component.data).toEqual(mockResponseData);});});
六、mock不需要的子组件
要 mock 掉不需要的子组件,可以使用
jest.mock
和
jest.fn
(或
jest.mock
中的
jest.fn
)来模拟子组件的导入。
下面是一个示例,演示如何 mock 掉不需要的子组件:
import React from'react';import{ shallow }from'enzyme';import ParentComponent from'../ParentComponent';import ChildComponent from'../ChildComponent';
jest.mock('../ChildComponent',()=> jest.fn(()=>null));describe('ParentComponent',()=>{it('should render without the ChildComponent',()=>{const wrapper =shallow(<ParentComponent />);expect(wrapper.find(ChildComponent)).toHaveLength(0);});});
在上述例子中,使用
jest.mock
来模拟
ChildComponent
的导入,并将其替换为一个匿名的空函数组件。这样,在渲染
ParentComponent
时,
ChildComponent
实际上会被替换为一个空的函数组件。
然后,可以使用
shallow
来浅渲染
ParentComponent
,并使用
expect
断言验证
ChildComponent
是否没有被渲染。
这样,就成功地 mock 掉了不需要的子组件,从而使测试集中关注于
ParentComponent
的逻辑和行为,而不会受到实际的子组件的影响。
版权归原作者 heiyay 所有, 如有侵权,请联系我们删除。