前言
在前端开发中,测试是非常重要的环节,而使用 Jest 测试 React 组件的过程中,经常需要 mock 外部依赖,保证测试的独立性和可重现性。
本文将介绍如何在使用 Jest 测试 React 组件时,mock 外部依赖,并提供详细的代码示例。
Jest 中的 Mock
在 Jest 中,mock 有两种形式:手动 mock 和自动 mock。
手动 mock 是指自己手写一个模拟模块,用于替换真实的模块,这种方式需要手动维护模拟模块的状态和行为。
自动 mock 是指使用 Jest 提供的自动模拟机制,它会自动创建一个模拟模块,实现模块中各个函数的行为并返回 Mockito 对象,这个过程可以通过 Jest 中的 jest.mock API 实现。
Mock 外部依赖
在测试 React 组件时,经常使用一些外部依赖(如:网络请求、localStorage 等),在测试过程中,我们希望能够 mock 这些外部依赖,保证测试的独立性和可重现性。
方式一:手动 mock 外部依赖
手动 mock 是最常见的 mock 方式。手动 mock 外部依赖的方式有两种:一种是使用 jest.fn() 创建一个 mock 函数,另一种是直接创建一个 mock 的对象或模块。
使用 jest.fn() 创建一个 mock 函数
例如,我们有一个组件 MyComponent,它使用了 axios 这个网络请求库,我们希望在测试中,能够 mock 掉 axios 的请求行为,可以使用 jest.fn() 创建一个简单的 mock 函数。
// www.javascriptcn.com code example
// MyComponent.jsx
import axios from 'axios';
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const result = await axios.get('/api/data');
setData(result.data);
};
fetchData();
}, []);
// ...
}
// MyComponent.test.jsx
import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';
jest.mock('axios', () => ({
get: jest.fn(() => Promise.resolve({ data: { message: 'mock data' } }))
}));
test('renders mock data', async () => {
render(<MyComponent />);
const message = await screen.findByText('mock data');
expect(message).toBeInTheDocument();
});在上面的例子中,我们使用 jest.mock() 函数,mock 了 axios 模块。它的第一个参数是模块名称('axios'),第二个参数是一个函数,该函数返回一个对象,该对象的 get 函数返回 Promise.resolve({ data: { message: 'mock data' } }),这样 MyComponent 组件就不会发送真实的网络请求,而是返回我们提供的 mock 数据。
直接创建一个 mock 的对象或模块
除了使用 jest.fn(),你还可以直接创建一个 mock 的对象或模块,然后在测试中手动注入该对象或模块。
例如,我们有一个组件 MyComponent2,它使用了一个 localStorage 对象,我们希望在测试时,能够 mock 掉 localStorage 对象,可以手动创建一个 mock。
// www.javascriptcn.com code example
// MyComponent2.jsx
function MyComponent2() {
const [data, setData] = useState(null);
useEffect(() => {
const storedData = localStorage.getItem('data');
setData(storedData);
}, []);
// ...
}
// MyComponent2.test.jsx
import { render, screen } from '@testing-library/react';
import MyComponent2 from './MyComponent2';
const localStorageMock = (() => {
let store = {};
return {
getItem: key => store[key] || null,
setItem: (key, value) => {
store[key] = value.toString();
},
removeItem: key => {
delete store[key];
},
clear: () => {
store = {};
}
};
})();
beforeEach(() => {
Object.defineProperty(window, 'localStorage', { value: localStorageMock });
});
test('renders mock localStorage data', async () => {
localStorage.setItem('data', 'mock data');
render(<MyComponent2 />);
const message = await screen.findByText('mock data');
expect(message).toBeInTheDocument();
});在上面的例子中,我们手动创建了一个模拟的 localStorage 对象,它包含了 getItem、setItem、removeItem 和 clear 函数,并注入到了 window 对象中,这样在 MyComponent2 组件中调用 localStorage.getItem('data') 时,就不是真正的 localStorage 了,而是我们提供的 mock 数据了。
方式二:自动 mock 外部依赖
除了手动 mock 外部依赖,Jest 还提供了自动 mock 的机制,它可以更方便地生成 mock 对象,并自动注入到测试代码中。
使用 jest.mock() 自动 mock 外部依赖
例如,我们有一个组件 MyComponent3,它使用了一个 moment.js 库,我们希望在测试中,能够 mock 掉 moment.js 的函数调用,可以使用 jest.mock() 自动生成一个模拟模块。
// www.javascriptcn.com code example
// MyComponent3.jsx
import moment from 'moment';
function MyComponent3() {
const [time, setTime] = useState(null);
useEffect(() => {
const currentTime = moment().format('MMMM Do YYYY, h:mm:ss a');
setTime(currentTime);
}, []);
// ...
}
// MyComponent3.test.jsx
import { render, screen } from '@testing-library/react';
import MyComponent3 from './MyComponent3';
jest.mock('moment', () => {
// mock 的 moment 函数返回固定的时间点,而不是当前时间
return () => ({
format: () => 'May 22nd 2021, 12:34:56 pm'
});
});
test('renders mock time', async () => {
render(<MyComponent3 />);
const time = await screen.findByText('May 22nd 2021, 12:34:56 pm');
expect(time).toBeInTheDocument();
});在上面的例子中,我们使用 jest.mock() 函数,mock 了 moment.js 模块。它的第一个参数是模块名称('moment'),第二个参数是一个函数,该函数返回一个对象,该对象的函数 format 返回固定的时间点,而不是当前时间,这样 MyComponent3 组件就不会获取真实的时间了,而是返回我们提供的 mock 数据。
结语
本文介绍了使用 Jest 测试 React 组件时,mock 外部依赖的方法,并提供了详细的代码示例。以上代码只是示例,实际情况下,我们需要根据项目的实际情况来选择使用手动 mock 还是自动 mock,并根据实际情况编写相应的 mock 代码。
测试是一个非常重要的环节,我们应该始终保持测试代码的质量,以便在项目开发中更好地发现和修复问题,提高代码可维护性和可扩展性。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d6a24fa941bf7134c69f60