如何Mock AWS SDK类以编写signUp函数的单元测试?
如何Mock AWS SDK v3的Cognito客户端进行单元测试
问题背景
现有一个基于AWS SDK v3的用户注册函数signUp,源码如下:
import { SignUpCommand, CognitoIdentityProviderClient, } from "@aws-sdk/client-cognito-identity-provider"; import { createClientForDefaultRegion } from "../../libs/utils/util-aws-sdk.js"; const signUp = async ({ clientId, username, password, email }) => { const client = createClientForDefaultRegion(CognitoIdentityProviderClient); const command = new SignUpCommand({ ClientId: clientId, Username: username, Password: password, UserAttributes: [{ Name: "email", Value: email }], }); return client.send(command); };
编写单元测试时尝试Mock AWS SDK,但测试仍调用真实实现,原Mock代码存在问题:
jest.mock('@aws-sdk/client-cognito-identity-provider', () => { return { CognitoIdentityProvider: class { send() { return new Promise((resolve, reject) => { resolve(expected) }) } }, SignUpCommand: class {} } })
错误分析
- 类名不匹配:源码中使用的是
CognitoIdentityProviderClient,但Mock里写的是CognitoIdentityProvider,导致Mock未生效 - 参数传递错误:
signUp接收的是单个对象参数,但测试用例里直接传了四个独立参数,函数无法正确解析 - 变量作用域问题:Mock工厂函数内部无法访问测试用例里的
expected变量 - 未Mock自定义工具函数:
createClientForDefaultRegion是自定义封装的函数,需要同步Mock它的返回值
正确实现方案
步骤1:修正AWS SDK的Mock
确保Mock的类名与源码一致,同时用jest.fn()来模拟send方法的返回值:
import { signUp } from './path-to-your-signUp-file'; import { createClientForDefaultRegion } from "../../libs/utils/util-aws-sdk.js"; // Mock AWS SDK jest.mock('@aws-sdk/client-cognito-identity-provider', () => { const mockSend = jest.fn(); return { CognitoIdentityProviderClient: jest.fn(() => ({ send: mockSend })), SignUpCommand: jest.fn((input) => input) // 保留命令参数以便后续验证 }; }); // Mock自定义工具函数 jest.mock('../../libs/utils/util-aws-sdk.js', () => ({ createClientForDefaultRegion: jest.fn((ClientClass) => new ClientClass()) }));
步骤2:编写正确的测试用例
调整参数传递为对象形式,同时验证Mock的调用情况:
describe('cognito', () => { it('should sign up a user', async () => { // 定义预期返回值 const expected = { UserConfirmed: true, UserSub: '1234567890' }; // 获取Mock的send方法 const mockSend = require('@aws-sdk/client-cognito-identity-provider').CognitoIdentityProviderClient.mock.results[0].value.send; // 设置send方法的异步返回值 mockSend.mockResolvedValue(expected); // 调用signUp,传入正确格式的对象参数 const result = await signUp({ clientId: '42', username: 'tony.stark@example.com', password: '1@m1r0nm@n', email: 'tony.stark@example.com' }); // 验证返回值符合预期 expect(result).toMatchObject(expected); // 验证SignUpCommand是否被传入正确参数 expect(require('@aws-sdk/client-cognito-identity-provider').SignUpCommand).toHaveBeenCalledWith({ ClientId: '42', Username: 'tony.stark@example.com', Password: '1@m1r0nm@n', UserAttributes: [{ Name: "email", Value: 'tony.stark@example.com' }] }); // 验证send方法是否被调用 expect(mockSend).toHaveBeenCalled(); }); });
关键说明
- 对于AWS SDK v3的客户端,需要Mock客户端类的构造函数,使其返回带有
send方法的模拟对象 - 自定义工具函数
createClientForDefaultRegion必须Mock,否则会创建真实的客户端实例 - 使用
mockResolvedValue模拟异步返回,比手动创建Promise更简洁 - 可通过验证
SignUpCommand的参数,确保业务逻辑传递的参数符合预期
内容的提问来源于stack exchange,提问作者Code-Apprentice
相关产品推荐
相关产品推荐

