本文介绍在 Vitest 测试中正确模拟 AWS SDK 的 S3Client 构造函数,使其在实例化时抛出指定异常,解决因自动替换原型导致原构造逻辑失效的问题。
本文介绍在 vitest 测试中正确模拟 aws sdk 的 `s3client` 构造函数,使其在实例化时抛出指定异常,解决因自动替换原型导致原构造逻辑失效的问题。
在单元测试中,有时需要验证代码对第三方客户端(如 @aws-sdk/client-s3 的 S3Client)初始化失败的健壮性——例如网络不可达、凭证缺失等场景下是否能正确捕获并处理异常。但直接修改 prototype.constructor(如通过 Object.defineProperty)在 Vitest 中通常无效,因为 Vitest 的 vi.mock() 机制会完全接管模块导出,且 constructor 属性本身不可靠地拦截 new 调用。
✅ 正确做法是:将 S3Client 类本身模拟为一个函数,并在其内部 throw 异常。Vitest 支持 vi.fn().mockImplementation(),其行为与 Jest 高度兼容,可精准控制构造行为:
// 在 test 文件顶部或 setup 文件中import { vi } from 'vitest'vi.mock('@aws-sdk/client-s3', async () => { // 可选:保留其他未被 mock 的导出(如命令类) const actual = await vi.importActual<typeof import('@aws-sdk/client-s3')>('@aws-sdk/client-s3') return { ...actual, // 关键:将 S3Client 替换为一个模拟构造函数 S3Client: vi.fn().mockImplementation(function (this: any, ...args: any[]) { // 注意:使用普通函数(而非箭头函数),以支持 this 绑定和 new 调用 throw new Error('Failed to instantiate S3Client: mocked constructor error') }), }})
⚠️ 注意事项:
✅ 验证示例:
import { S3Client } from '@aws-sdk/client-s3'describe('S3Client instantiation failure handling', () => { test('should throw when creating S3Client instance', () => { expect(() => { new S3Client({ region: 'us-east-1' }) }).toThrow('Failed to instantiate S3Client: mocked constructor error') }) // 还可验证你的业务逻辑是否正确捕获该错误 test('service should handle S3Client construction failure', async () => { await expect(async () => { // 假设 yourService.createBucket() 内部 new S3Client(...) await yourService.createBucket('test-bucket') }).rejects.toThrow('Failed to instantiate S3Client') })})
? 小结:不要试图劫持原型上的 constructor,而应直接 mock 类的导出值本身。这是 Vitest/Jest 官方推荐且最可靠的构造函数异常模拟方式,语义清晰、行为确定、易于维护。