NestJS如何Mock作用域包?E2E测试Mock失效求助
问题描述
我有一个作用域包@my-company/common,包含多个模块(如@my-company/common/dist/services/aws/s3、@my-company/common/dist/services/mongodb/atlas),其中既有@Injectable类也有非可注入类。在控制器的E2E测试中,我需要Mock这些模块。
示例业务代码MyAwsService:
import { MyS3Service } from '@my-company/common/dist/services/aws/s3'; @Injectable() export class MyAwsService{ private s3: MyS3Service; constructor(private config: ConfigService) { const configuration = { region: config.get('region'), credentials: { accessKeyId: config.get('key'), secretAccessKey: config.get('secret'), }, }; this.s3 = new MyS3Service(configuration); } }
测试时我不想提供真实凭证,只想Mock整个MyS3Service。按照Jest文档,我在测试文件顶部添加了:
jest.mock('../node_modules/@my-company/common/dist/services/aws/s3/s3.service.js');
同时创建了__mocks__/@my-company/common.js文件,内容如下:
export const getListOfFilesFn = jest.fn().mockImplementation(() => Promise.resolve({ status: 'OK' })); export const MyS3Service = jest.fn().mockImplementation(function () { return { getListOfFiles: () => { console.log(`mock getListOfFiles called`); return getListOfFilesFn; }, }; });
现在的问题是:移除jest.mock时,测试会因S3需要凭证失败;保留后测试能运行,但自定义Mock的MyS3Service没有返回值,也不打印日志,所有响应都是undefined。想知道为什么自定义Mock不生效,当前实际运行的是什么?
问题原因
Mock路径不匹配:你指定的
jest.mock路径是物理文件路径../node_modules/@my-company/common/dist/services/aws/s3/s3.service.js,但Jest匹配Mock的逻辑是基于模块的导入路径,而非物理文件路径。这种情况下,Jest不会关联到你在__mocks__下创建的作用域包Mock文件。Mock模块结构不匹配:原模块的导出路径是
@my-company/common/dist/services/aws/s3(通常是从该目录的index文件导出MyS3Service),但你的Mock文件直接放在__mocks__/@my-company/common.js,和原模块的导出结构不对应,导致导入时找不到你定义的Mock成员。
解决方案
步骤1:修正jest.mock调用
直接使用模块的导入路径进行Mock,而非物理文件路径:
jest.mock('@my-company/common/dist/services/aws/s3');
这样Jest会自动按照模块路径去查找__mocks__下对应的Mock文件。
步骤2:调整Mock文件结构
按照原模块的路径层级创建Mock目录:
- 在项目根目录下创建
__mocks__/@my-company/common/dist/services/aws/s3目录 - 在该目录下创建
index.ts(或.js,根据项目文件类型),内容如下:
export const getListOfFilesFn = jest.fn().mockResolvedValue({ status: 'OK' }); export const MyS3Service = jest.fn().mockImplementation(() => ({ getListOfFiles: () => { console.log(`mock getListOfFiles called`); return getListOfFilesFn; }, }));
这种结构和原模块的导出路径完全匹配,Jest能正确加载你定义的Mock逻辑。
步骤3:验证Mock是否生效
在测试文件中可以添加验证代码,确认Mock已被正确加载:
import { MyS3Service } from '@my-company/common/dist/services/aws/s3'; test('Mock is correctly loaded', () => { expect(MyS3Service).toBeDefined(); expect(jest.isMockFunction(MyS3Service)).toBe(true); });
额外说明
当前测试能运行但Mock不生效,是因为Jest在找不到对应自定义Mock的情况下,自动生成了一个空Mock——所有导出成员都是默认的jest.fn(),没有任何自定义逻辑,所以调用后返回undefined,也不会执行你写的日志和返回值逻辑。
内容的提问来源于stack exchange,提问作者Naryck

