如何在Jest单元测试中提供自定义类型S3ServiceConstructor
问题
编写S3Service的Jest单元测试时,尝试为S3Service提供自定义类型S3ServiceConstructor并配置到providers中出现报错,求正确配置方式。
S3服务代码
import { IAWS } from '../AWS'; import { S3 } from 'aws-sdk'; export interface S3ServiceConstructor { pathDelimiter?: string; } export class S3Service { private s3: S3; private readonly pathDelimiter: string; constructor( { pathDelimiter }: S3ServiceConstructor, options?: S3.Types.ClientConfiguration ) { this.pathDelimiter = pathDelimiter || '/'; this.s3 = new IAWS.S3(options); } }
测试文件代码
import { Test } from '@nestjs/testing'; export interface S3ServiceConstructor { pathDelimiter?: string; } import { S3Service } from './s3.service'; describe('S3 Service', () => { let service: S3Service; const mockS3Instance = { upload: jest.fn().mockReturnThis(), promise: jest.fn().mockReturnThis(), catch: jest.fn() } jest.mock('aws-sdk', () => { return jest.fn(() => mockS3Instance) }); beforeEach(async () => { const moduleRef = await Test.createTestingModule({ providers: [ S3Service, provide: [] // 此处配置? ] }).compile(); service = moduleRef.get<S3Service>(S3Service); }); afterEach(() => { jest.clearAllMocks(); }); it('getXYZ should be called with correct parameters', async () => { // .... }); });
正确配置方式
方案一:适配Nest依赖注入(推荐长期维护)
- 清理重复类型:删掉测试文件里重复定义的
S3ServiceConstructor,直接从s3.service导入,避免类型冲突。 - 给构造参数加注入标识:TypeScript接口编译后会消失,Nest无法识别接口作为注入token,得给S3Service的构造参数加
@Inject装饰器,用字符串作为唯一标识。 - 正确Mock依赖的IAWS.S3:原mock没覆盖
IAWS.S3,调整mock逻辑让new IAWS.S3()返回预设的mock实例。
修改后的S3Service代码
import { Inject, Optional } from '@nestjs/common'; import { IAWS } from '../AWS'; import { S3 } from 'aws-sdk'; export interface S3ServiceConstructor { pathDelimiter?: string; } export class S3Service { private s3: S3; private readonly pathDelimiter: string; constructor( @Inject('S3_SERVICE_OPTIONS') { pathDelimiter }: S3ServiceConstructor, @Optional() @Inject('S3_CLIENT_CONFIG') options?: S3.Types.ClientConfiguration ) { this.pathDelimiter = pathDelimiter || '/'; this.s3 = new IAWS.S3(options); } }
对应的测试文件代码
import { Test } from '@nestjs/testing'; import { S3Service, S3ServiceConstructor } from './s3.service'; // Mock IAWS模块,覆盖S3构造函数 jest.mock('../AWS', () => ({ IAWS: { S3: jest.fn(() => ({ upload: jest.fn().mockReturnThis(), promise: jest.fn().mockResolvedValue({ Location: 'test-url' }), // 按需添加其他需要mock的方法 })), }, })); describe('S3 Service', () => { let service: S3Service; beforeEach(async () => { const moduleRef = await Test.createTestingModule({ providers: [ S3Service, // 提供S3_SERVICE_OPTIONS的具体配置值 { provide: 'S3_SERVICE_OPTIONS', useValue: { pathDelimiter: '-' } as S3ServiceConstructor, }, // 可选的S3客户端配置,标记为optional避免启动报错 { provide: 'S3_CLIENT_CONFIG', useValue: { region: 'us-east-1' }, optional: true, }, ], }).compile(); service = moduleRef.get<S3Service>(S3Service); }); afterEach(() => { jest.clearAllMocks(); }); it('should initialize with correct path delimiter', () => { // 可以给S3Service加个getter方法暴露私有属性,比如: // getPathDelimiter() { return this.pathDelimiter; } // expect(service.getPathDelimiter()).toBe('-'); }); it('getXYZ should be called with correct parameters', async () => { // 你的测试逻辑 }); });
方案二:直接实例化服务(快速测试无需改原代码)
如果不想改动原S3Service的代码,直接在测试里用useFactory手动创建实例,传入构造参数即可:
import { Test } from '@nestjs/testing'; import { S3Service } from './s3.service'; // Mock IAWS模块,覆盖S3构造函数 jest.mock('../AWS', () => ({ IAWS: { S3: jest.fn(() => ({ upload: jest.fn().mockReturnThis(), promise: jest.fn().mockResolvedValue({ Location: 'test-url' }), })), }, })); describe('S3 Service', () => { let service: S3Service; beforeEach(async () => { const moduleRef = await Test.createTestingModule({ providers: [ { provide: S3Service, useFactory: () => { // 手动传入构造参数 return new S3Service({ pathDelimiter: '-' }, { region: 'us-east-1' }); }, }, ], }).compile(); service = moduleRef.get<S3Service>(S3Service); }); afterEach(() => { jest.clearAllMocks(); }); it('getXYZ should be called with correct parameters', async () => { // 测试逻辑 }); });
内容的提问来源于stack exchange,提问作者XCEPTION
相关产品推荐
相关产品推荐

