
Jest 单元测试中,若在多个 test case 间复用同一份 mutable mock 数据(如数组),因 shift() 等操作会改变原数组引用,导致后续用例读取到已被修改的数据。解决方法是:每个测试独立创建 mock 数据、确保异步函数正确 await、并将 jest.mock() 提升至模块顶层。
jest 单元测试中,若在多个 test case 间复用同一份 mutable mock 数据(如数组),因 `shift()` 等操作会改变原数组引用,导致后续用例读取到已被修改的数据。解决方法是:每个测试独立创建 mock 数据、确保异步函数正确 await、并将 `jest.mock()` 提升至模块顶层。
在 Jest 测试中,共享可变数据对象是导致测试污染(test pollution)的常见根源。你遇到的问题——第二个测试用例返回 id: 2 而非预期的 id: 1——正是因为两个 it 块共用了同一个 mockData 数组引用,而 data.shift() 在第一个测试中已将原数组首位元素移除,使第二个测试拿到的已是 [ { id: 2 }, { id: 3 } ]。
✅ 正确做法三要素
-
每个测试用例独立声明 mock 数据
避免 const mockData = [...] 定义在 describe 作用域内(即测试套件顶层)。应将其移至每个 it 块内部,确保每次执行都获得全新数组实例:it('Should return a id 1', async () => { const mockData = [{ id: 1 }, { id: 2 }, { id: 3 }]; // ✅ 每次新建 getData.mockResolvedValue(mockData); const response = await Helpers.execute(); expect(response.id).toBe(1); }); -
严格处理异步逻辑:await 不可省略
getData() 是 async 函数,必须 await getData() 获取实际数据;否则 data.shift() 将作用于 Promise 对象而非解析后的数组,引发运行时错误或逻辑错乱:// helpers.js(修正后) export class Helpers { static async execute(request) { if (request) await track(); // ✅ await 异步调用 const data = await getData(); // ✅ 关键:await 获取真实数组 return data.shift(); } } -
jest.mock() 必须置于模块顶层(import 后、describe 前)
Jest 的自动模拟(auto-mock)机制要求 jest.mock('./operations') 在模块加载阶段生效。若放在 beforeAll 中,不仅破坏模拟时机,还可能因执行顺序问题导致未生效或重复模拟:// helpers.test.js(推荐结构) import { Helpers } from './helpers'; import { getData, track } from './operations'; jest.mock('./operations'); // ✅ 模块顶层,确保所有测试前完成模拟 describe('Helpers', () => { afterEach(() => { jest.clearAllMocks(); // 清理各 mock 函数的调用记录与返回值 }); // ... test cases });
⚠️ 补充注意事项
- jest.clearAllMocks() 在 afterEach 中足够,无需 beforeAll 中调用 jest.clearAllMocks() 或 jest.mock() —— 后者甚至会覆盖顶层 jest.mock() 的效果。
- 若 operations.js 中函数未显式 export,需先修正导出方式(如 export async function getData() {...}),否则 jest.mock() 无法正确代理。
- 对于更复杂状态(如嵌套对象或需多次调用返回不同值),可使用 mockResolvedValueOnce() 实现精确控制:
getData.mockResolvedValueOnce([{id: 1}]).mockResolvedValueOnce([{id: 1}, {id: 2}]);
遵循以上实践,即可彻底隔离测试用例间的副作用,保障 Jest 测试的稳定性、可重复性与可维护性。











