react+jest+enzyme配置及编写前端单元测试UT

原文合集地址如下,有需要的朋友可以关注

本文地址

合集地址

文章目录

安装及配置

  1. 安装相关库:
    首先,使用npm或yarn安装所需的库。
bash 复制代码
npm install --save-dev jest enzyme enzyme-adapter-react-16 enzyme-to-json
  1. 配置Jest:
    在项目根目录下创建一个jest.config.js文件,并配置Jest。
javascript 复制代码
// jest.config.js
module.exports = {
  setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'],
  testEnvironment: 'jsdom',
  moduleNameMapper: {
    '\\.(css|less)$': 'identity-obj-proxy',
  },
};

在上述配置中,设置了setupFilesAfterEnvsrc/setupTests.js,指定了测试环境为jsdom,并设置了模块名称映射以处理CSS和LESS文件。

  1. 创建setupTests.js文件:
    在项目的src目录下创建一个setupTests.js文件,并进行Enzyme的初始化和配置。
javascript 复制代码
// src/setupTests.js
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

Enzyme.configure({ adapter: new Adapter() });

在上述代码中,导入Enzyme和Enzyme适配器,并使用适配器初始化Enzyme。

  1. 编写测试用例:
    在测试目录中创建一个与要测试的组件对应的测试文件,并编写测试用例。
js 复制代码
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来查找组件中的元素,并进行断言验证。

  1. 运行测试:
    在终端中运行测试命令,以执行测试用例。
bash 复制代码
npm run test

Jest将自动查找并执行项目中以.test.js.spec.js结尾的文件中的测试用例。

enzyme渲染

Enzyme提供了三种常用的渲染方式,分别是shallowmountrender。这些渲染方式在测试组件时具有不同的特点和用途。

  1. shallow 渲染:
    shallow 渲染是Enzyme的浅层渲染方式,它仅渲染被测试组件本身,而不渲染其子组件。
js 复制代码
import { shallow } from 'enzyme';
import YourComponent from '../YourComponent';

test('测试组件渲染', () => {
  const wrapper = shallow(<YourComponent />);
  // 对被测试组件进行断言和验证
});

使用shallow渲染时,被测试组件的子组件将被替换为占位符,并且无法直接访问子组件。这使得测试更加轻量且快速,适用于对被测试组件本身的行为进行测试。

  1. mount 渲染:
    mount 渲染是Enzyme的全渲染方式,它会将被测试组件及其子组件完全渲染到真实的DOM中。这使得可以模拟实际的组件嵌套和交互,并对其进行全面的测试。
js 复制代码
import { mount } from 'enzyme';
import YourComponent from '../YourComponent';

test('测试组件渲染', () => {
  const wrapper = mount(<YourComponent />);
  // 对被测试组件及其子组件进行断言和验证
});

使用mount渲染时,被测试组件及其子组件将在真实的DOM中进行渲染和交互。这使得可以测试组件的交互、生命周期方法和子组件的行为。

  1. render 渲染:
    render 渲染是Enzyme的静态渲染方式,它将组件渲染为静态的HTML字符串,不涉及真实的DOM操作。这使得可以对组件的输出进行快照测试,以验证其渲染结果是否符合预期。
js 复制代码
import { render } from 'enzyme';
import YourComponent from '../YourComponent';

test('测试组件渲染', () => {
  const wrapper = render(<YourComponent />);
  // 对组件输出的HTML进行断言和验证
});

使用render渲染时,被测试组件及其子组件将以静态HTML字符串的形式输出,可以方便地进行快照测试。

下面的例子中使用到的渲染方式只是举例,结合实际情况使用mount、shalldow或者render都是可以的。

测试技巧

一、常见测试

  1. 示例:测试组件渲染和属性传递
    假设有一个简单的React组件 Button,接收一个label属性,并在按钮上显示该标签。可以编写一个测试用例来验证组件是否正确渲染和传递属性。
js 复制代码
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组件,并使用expecttoBe断言验证按钮上的文本是否与传递的标签相匹配。

  1. 示例:模拟事件处理
    假设有一个表单组件 LoginForm,其中包含一个提交按钮。可以编写一个测试用例来模拟按钮点击事件,并验证相应的事件处理函数是否被调用。
js 复制代码
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方法模拟按钮的点击事件。然后,使用expecttoHaveBeenCalledTimes断言验证onSubmit函数是否被调用了一次。

  1. 示例:异步操作的处理
    如果组件中包含异步操作,可以使用async/awaitdone回调函数来处理异步测试。
js 复制代码
import React from 'react';
import { mount } from 'enzyme';
import AsyncComponent from '../AsyncComponent';

describe('AsyncComponent', () => {
  it('renders data correctly after fetching', async () => {
    const fetchData = () => {
      return new Promise((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的组件时,可以通过模拟事件或直接调用组件的方法来触发各种操作。下面是一些常见组件的触发方法示例:

  1. Input 输入框:
javascript 复制代码
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表示输入的值。

  1. Select 选择器:
javascript 复制代码
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 事件来选择该选项。

  1. Checkbox 复选框:
jsx 复制代码
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表示复选框的选中状态。

  1. Dropdown 下拉菜单:
javascript 复制代码
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事件。

  1. 模拟文件上传
    在React中,由于安全性的限制,不能直接模拟文件上传的完整流程。但是,可以通过模拟触发change事件来模拟用户选择文件的行为。这样可以触发相应的文件上传逻辑。

以下是一个示例代码,演示了如何使用simulate方法来模拟文件上传:

js 复制代码
import { mount } from 'enzyme';

test('模拟文件上传', () => {
  const onFileUpload = jest.fn(); // 模拟文件上传时调用的函数

  const wrapper= mount(<YourComponent onFileUpload={onFileUpload} />);

  // 创建一个模拟的File对象
  const file = new File(['文件内容'], '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的组件的单元测试时,可以采取以下措施以确保测试不会报错:

  1. 模拟Redux Store:
    在测试中,可以创建一个模拟的Redux Store,并将其传递给组件作为属性。可以使用类似于redux-mock-store的库来创建模拟的Store。
javascript 复制代码
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作为属性。

  1. 使用Provider组件:
    在测试中,使用Redux的Provider组件将模拟的Store包装在组件的外部。这样可以确保组件在测试中能够访问到Redux的状态和行为。
javascript 复制代码
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。

  1. 测试Redux相关的行为:
    在编写单元测试时,除了测试组件的渲染和行为外,还可以编写专门测试Redux相关行为的测试用例。例如,测试Redux的action创建函数、reducer函数和异步操作的处理等。

四、使用路由的组件

当编写涉及React Router的组件的单元测试时,可以采取以下措施来处理受到影响的组件:

  1. 使用MemoryRouter:
    在测试中,使用MemoryRouter来包装被测试的组件。MemoryRouter是React Router提供的一个用于测试的特殊路由器组件,它在内存中维护路由状态,不会对浏览器URL进行实际更改。
javascript 复制代码
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将被测试的组件包装在内部,以便在测试中模拟路由。

  1. 使用StaticRouter:
    如果组件使用了Server-Side Rendering(SSR),可以使用StaticRouter来模拟服务器端渲染的路由。
javascript 复制代码
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属性设置当前的路径。

  1. 使用BrowserRouterRouter
    如果组件使用了BrowserRouter或自定义的Router组件,可以在测试中直接使用这些组件,并通过history属性模拟路由历史记录。
javascript 复制代码
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接口网络请求

  1. mock 接口请求
javascript 复制代码
// @/servise/XXX 是我自己封装的功能请求的相对地址
// 方法一
jest.mock('@/servise/XXX', ()=>({
getData: Promise.resove({res:{}})
));
// 方法二
import service from '@/service/XXX';
service.getData= ()=>Promise.resolve({res:{}})

2.自定义模拟的请求和返回数据

javascript 复制代码
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 = new YourComponent();
    await component.fetchData();

    // 验证结果
    expect(component.data).toEqual(mockResponseData);
  });
});

六、mock不需要的子组件

要 mock 掉不需要的子组件,可以使用 jest.mockjest.fn(或 jest.mock 中的 jest.fn)来模拟子组件的导入。

下面是一个示例,演示如何 mock 掉不需要的子组件:

javascript 复制代码
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 的逻辑和行为,而不会受到实际的子组件的影响。

相关推荐
黄尚圈圈28 分钟前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水1 小时前
简洁之道 - React Hook Form
前端
正小安3 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch5 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光5 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   5 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   5 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
搁浅°8795 小时前
spring6启用Log4j2日志
单元测试·log4j
Fan_web5 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常5 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式