Angular v16 Jest预览版搭配ESBuild,如何正确Mock JS模块?
Angular 16 Jest开发者预览版模块Mock解决方案
针对ESBuild构建导致的传统Jest Mock方法失效问题,以下是几个可行的解决思路:
1. 适配ESBuild的jest.mock写法
ESBuild会对模块进行树摇、重命名甚至内联处理,需要确保Mock语法符合ES模块规范:
- 针对命名导出的模块,Mock时需添加
__esModule: true标识:
jest.mock('module-name', () => ({ __esModule: true, ...jest.requireActual('module-name'), // 保留其他原有导出 targetMethod: jest.fn(() => 'mock-result') }));
- 针对默认导出,需显式指定
default属性:
jest.mock('module-name', () => ({ __esModule: true, default: jest.fn(() => 'default-mock-result') }));
2. 利用Angular依赖注入替换模块逻辑
如果被Mock的模块是Angular服务或可封装为服务的逻辑,优先使用Angular的测试依赖注入机制:
- 把目标模块的功能封装为Angular服务(比如
TargetService) - 在测试用例中,用
TestBed.overrideProvider提供Mock实现:
TestBed.configureTestingModule({ providers: [ { provide: TargetService, useValue: { targetMethod: jest.fn(() => 'mock-value') } } ] });
这种方式完全适配Angular的构建流程,不会受ESBuild的模块处理影响。
3. 修改ESBuild配置保留模块标识
在Jest配置中调整ESBuild的转换选项,避免模块被内联或重命名:
在jest.config.ts(或jest.config.js)中,针对Angular builders的ESBuild配置添加如下参数:
export default { transform: { '^.+\\.(ts|js)$': [ '@angular-builders/jest', { esbuildOptions: { keepNames: true, // 保留函数/类名称 preserveModules: true // 不将模块内联到调用文件 } } ] } };
配置后,传统的jest.mock和jest.spyOn大概率能恢复正常,但可能会牺牲部分ESBuild的构建优化。
针对jest.spyOn不可写的临时解决方案
如果必须使用spyOn,可以先通过Object.defineProperty将目标方法改为可写:
import * as module from 'module-name'; // 先修改方法的可写性 Object.defineProperty(module, 'targetMethod', { writable: true }); // 再进行spyOn const spy = jest.spyOn(module, 'targetMethod').mockReturnValue('mock-result');
这种方式属于hack手段,仅建议在临时场景使用,长期还是推荐前面的规范方案。
内容的提问来源于stack exchange,提问作者Sergey
相关产品推荐
相关产品推荐

