
在 Jest 中,__mocks__ 文件的导出变量默认是模块级常量,无法直接修改;但可通过在测试作用域内声明同名局部变量实现安全、隔离的临时覆盖,无需修改原始 mock 或破坏其他测试。
在 jest 中,`__mocks__` 文件的导出变量默认是模块级常量,无法直接修改;但可通过在测试作用域内声明同名局部变量实现安全、隔离的临时覆盖,无需修改原始 mock 或破坏其他测试。
Jest 的 __mocks__ 机制会在模块解析时自动替换目标模块,但其导出内容(尤其是 const 或 let 声明)在导入后即成为只读引用或独立绑定值。因此,试图通过 jest.spyOn() 修改顶层变量、或使用 Object.defineProperty()/Proxy 拦截模块导出,通常无效——因为 Jest 的自动 mock 是基于 ES 模块静态导入的,变量在 import 时已求值并绑定,后续无法劫持。
✅ 推荐方案:作用域内局部变量遮蔽(Shadowing)
这是最简洁、可靠且符合 Jest 设计哲学的方式:不在 mock 文件中修改全局状态,而是在特定测试用例的作用域内定义同名局部变量。由于 JavaScript 的作用域链优先级,该局部变量会自然覆盖从 __mocks__ 导入的同名标识符,且仅影响当前 it() 块,完全隔离、无副作用。
例如,假设你的 mock 文件如下:
// __mocks__/example.ts export const token: string | null = "exampleString"; // 推荐用 const 替代 let,更安全
在测试文件中,可这样灵活使用:
// __tests__/foo.spec.ts
import { token } from '../__mocks__/example.ts';
import foo from '../foo.ts';
describe('foo', () => {
it('uses default token', () => {
expect(foo.doSomething(token)).toBe(/* expected */);
});
it('uses null token for edge case', () => {
const token = null; // ✅ 局部变量遮蔽导入的 token
expect(foo.doSomethingElse(token)).toBe(/* expected */);
});
it('uses custom token', () => {
const token = "custom-test-token";
expect(foo.authenticate(token)).toBe(true);
});
});⚠️ 注意事项:
- 确保 __mocks__/example.ts 路径与被 mock 的原始模块路径严格匹配(如原模块为 ../modules/example.ts,则 __mocks__ 文件应位于同级 __mocks__/example.ts);
- 若原始模块导出的是 let 变量(如 export let token = ...),不建议直接修改它——因为 Jest 的 mock 是编译时静态替换,运行时修改可能因模块缓存失效或跨测试污染导致不可靠;
- 避免使用 jest.unmock() 尝试“回退”到真实实现:这会绕过整个 mock 逻辑,与本场景目标(仅覆盖变量值)无关,且易引发依赖未 mock 的副作用;
- 如需更复杂的 mock 行为(如函数返回值动态变化),应将变量封装为 getter 函数(如 export const getToken = () => token),再配合 jest.mock() 工厂函数重写,但对简单值覆盖,遮蔽法更轻量、直观。
总结:Jest 测试的本质是控制输入、验证输出。当变量只是测试输入之一时,最健壮的做法是显式传入所需值(如 foo.doSomething(null)),而非依赖全局 mock 状态。局部遮蔽既是技术捷径,也体现了清晰、可维护的测试设计原则。

















