Jest单元测试mock SQSService返回undefined解决方法
Jest单元测试mock SQSService方法返回undefined问题修复
环境信息
- 测试框架:Jest
^26.4.2 - mock工具:jest-mock-extended
^2.0.6 - 问题现象:测试文件内直接调用mock的
getSqsQueueUrl方法可正常返回预设值,被测系统内部调用该方法时返回undefined,代码可正常编译。
现有代码
测试文件代码
jest.mock('aws-sdk'); jest.mock('../../../lambda-resources/AWS/Services/SQSService'); import { SQS, Request, Service, AWSError } from 'aws-sdk'; import { mockDeep, DeepMockProxy } from 'jest-mock-extended'; import { GetQueueUrlResult } from 'aws-sdk/clients/sqs'; import { SQSService } from '../../../lambda-resources/AWS/Services/SQSService'; let sqsMocked: DeepMockProxy<SQS>; let sqsServiceMocked: DeepMockProxy<SQSService>; beforeEach(async () => { sqsMocked = mockDeep<SQS>(); sqsServiceMocked = mockDeep<SQSService>(); sqsMocked.getQueueUrl.mockReturnValue(new Request<GetQueueUrlResult, AWSError>(new Service(), 'test')); sqsServiceMocked.getSqsQueueUrl.mockResolvedValue(Promise.resolve('test')); }) test('getSqsQueueUrl should...', async () => { // 测试文件内直接调用可正常返回预设值,被测系统内部调用时返回undefined expect(await sqsServiceMocked.getSqsQueueUrl('a')).toBe('test'); await (await LeadsCreator({}, {})) // ...其余测试逻辑 })
被测业务代码
const AWSSqs = new AWS.SQS(); const _SqsService = new SQSService(AWSSqs); const response = await _SqsService.getSqsQueueUrl("x"); // 此处response实际取值为undefined
根因分析
jest.mock('SQSService对应路径')会自动mock整个SQSService类,但beforeEach中手动通过mockDeep创建的sqsServiceMocked是独立的mock实例,未被Jest注入到被测代码的模块依赖体系中。被测代码执行new SQSService()时拿到的是Jest自动生成的默认mock实例,而非手动配置了返回值的sqsServiceMocked。默认mock的异步方法无预设返回值时会返回undefined,因此出现测试文件内直接调用mock正常、业务代码内调用返回空的现象。
不需要额外将sqsMocked注入sqsServiceMocked,核心是让被测代码实例化SQSService时,拿到的是配置了mock规则的实例。
修复方案
改造beforeEach逻辑,将手动创建的mock实例绑定到Jest自动生成的类mock上,无需修改业务代码:
beforeEach(() => { // 清空之前用例的mock状态,避免用例间污染 jest.clearAllMocks(); sqsMocked = mockDeep<SQS>(); sqsServiceMocked = mockDeep<SQSService>(); // *关键步骤*:配置new SQSService()时返回我们自定义规则的mock实例 const MockedSQSService = SQSService as jest.Mock; MockedSQSService.mockReturnValue(sqsServiceMocked); // 配置mock返回值,mockResolvedValue本身会包装Promise,无需额外套Promise.resolve sqsMocked.getQueueUrl.mockReturnValue(new Request<GetQueueUrlResult, AWSError>(new Service(), 'test')); sqsServiceMocked.getSqsQueueUrl.mockResolvedValue('test'); })
注意事项
- 所有
jest.mock语句必须写在import语句最顶部,因为被测代码中new AWS.SQS()、new SQSService()是模块加载时就执行的顶层代码,import后再调用mock会出现mock不生效问题。 - 如果业务代码后续需要用到SQS实例的其他方法,可通过
MockedSQSService的mock.calls拿到实例化时传入的sqs参数,校验传入实例是否符合预期。
内容的提问来源于stack exchange,提问作者Hoppe
相关产品推荐
相关产品推荐

