Jest测试跳过S3 getObject调用及异常通过问题排查
问题原因
问题确实出在S3 mock配置和测试用例写法两处错误:
- 你的业务代码用的是aws-sdk的回调风格调用
getObject,调用时第二个参数传入了回调函数,但你写的mock里getObject被设置为jest.fn().mockReturnThis(),调用后只会返回mock实例本身,完全没有处理传入的回调参数,回调永远不会被触发,所以Promise一直处于pending状态,回调内的Checkpoint 2日志自然不会打印。 - 测试用例中写
expect(output).resolves.toEqual('blah')时没有加await,Jest执行完测试函数最后一行就会直接标记测试通过,不会等待pending的Promise变更状态,所以哪怕断言预期值完全错误,也会出现误判通过的情况。
修复方案
你可以根据自己的代码风格选两种修复方式:
方案1:保留现有回调风格业务代码,修正mock逻辑
把mock的getObject改为支持触发传入的回调,同时测试用例中等待Promise执行完成再断言,修正后的测试代码如下:
const s3Handler = require('../s3') const AWS = require('aws-sdk') jest.mock('aws-sdk', () => { const mockS3 = { getObject: jest.fn(), } return { config: { update() { return {}; } }, S3: jest.fn(() => mockS3) } }) describe('S3 get object test', () => { afterEach(() => { jest.clearAllMocks() }) test('should return s3 object content when call success', async () => { const mockS3 = new AWS.S3(); const mockFileContent = 's3file.js' // 模拟getObject逻辑:接收参数后立刻执行回调返回成功结果 mockS3.getObject.mockImplementation((params, callback) => { callback(null, mockFileContent) }) const output = await s3Handler.getS3Object('test-bucket', 'test-key'); expect(output).toEqual(mockFileContent) // 校验调用参数是否正确 expect(mockS3.getObject).toHaveBeenCalledWith( { Bucket: 'test-bucket', Key: 'test-key' }, expect.any(Function) ) }) test('should throw error when s3 call failed', async () => { const mockS3 = new AWS.S3(); const mockError = new Error('S3 access denied') // 模拟调用报错场景 mockS3.getObject.mockImplementation((params, callback) => { callback(mockError, null) }) await expect(s3Handler.getS3Object('test-bucket', 'test-key')).rejects.toThrow(mockError) }) })
方案2:改业务代码为aws-sdk原生Promise调用风格
aws-sdk原生支持.promise()方法把回调风格调用转为Promise,不需要自己手动包Promise封装,代码更简洁,适配你最开始写的promise风格mock:
修正后的业务代码:
const AWS = require('aws-sdk'); const s3 = new AWS.S3({ apiVersion: '2012-11-05 ' }) async function getS3Object(bucket, file) { const parameters = { Bucket: bucket, Key: file } return s3.getObject(parameters).promise() } module.exports = { getS3Object }
对应的测试用例记得给Promise断言加await即可:
const s3Handler = require('../s3') const AWS = require('aws-sdk') jest.mock('aws-sdk', () => { const mockS3 = { getObject: jest.fn().mockReturnThis(), promise: jest.fn() } return { config: { update() { return {}; } }, S3: jest.fn(() => mockS3) } }) describe('S3 get object test', () => { test('test', async () => { const mockS3 = new AWS.S3(); const mockResult = 's3file.js' mockS3.promise.mockResolvedValueOnce(mockResult); const output = await s3Handler.getS3Object('test-bucket', 'test-key'); expect(output).toEqual(mockResult) expect(mockS3.getObject).toHaveBeenCalledWith({ Bucket: 'test-bucket', Key: 'test-key' }) }) })
注意:所有Jest中涉及异步Promise的断言,要么在
expect前加await,要么把Promise直接return出测试函数,否则会出现测试提前退出、断言不生效的问题。
内容的提问来源于stack exchange,提问作者Mohan Aravind
相关产品推荐
相关产品推荐

