Jest模拟amazon-cognito-identity-js的authenticateUser提示未定义问题
问题:Jest模拟Amazon Cognito时出现
TypeError: cognitoUser.authenticateUser is not a function 被测代码
const getNewCognitoUser = (username) => { const userData = { Username: username, Pool: userPool, }; return new CognitoUser(userData); }; const setAWSCredentials = (jwt) => { return new Promise((resolve, reject) => { AWS.config.credentials = new AWS.CognitoIdentityCredentials({ IdentityPoolId: appConfig.IdentityPoolId, Logins: { [appConfig.Logins.cognito.identityProviderName]: jwt, }, }); AWS.config.credentials.clearCachedId(); AWS.config.credentials.refresh((err) => { err ? reject(err) : resolve(); }); }); }; export const authenticate = (username, password) => { const authenticationDetails = new AuthenticationDetails({ Username: username, Password: password, }); cognitoUser = getNewCognitoUser(username); return new Promise((resolve, reject) => { cognitoUser.authenticateUser(authenticationDetails, { onSuccess: function (result) { setAWSCredentials(result.idToken.jwtToken) .then(() => { resolve(result); }) .catch((err) => { reject(err); }); }, onFailure: function (err) { reject(err); }, newPasswordRequired: function (userAttributes, requiredAttributes) { reject({ code: "PasswordResetRequiredException", message: "New Password Required", newPasswordRequired: true, }); }, }); }); };
测试代码
import { authenticate } from "../user"; import { CognitoUser } from "amazon-cognito-identity-js"; import AWS from "aws-sdk"; jest.mock("amazon-cognito-identity-js", () => { const mCognitoUser = { authenticateUser: jest.fn((authenticationDetails, callbacks) => { if (callbacks && callbacks.onSuccess) { // Simulate a successful authentication callbacks.onSuccess({ idToken: { jwtToken: "fakeToken" } }); } }), }; return { CognitoUser: jest.fn(() => mCognitoUser), CognitoUserPool: jest.fn(), AuthenticationDetails: jest.fn(), CognitoUserAttribute: jest.fn(), }; }); jest.mock("aws-sdk", () => { const mCognitoIdentityCredentials = jest.fn().mockImplementation(() => { return { clearCachedId: jest.fn(), refresh: jest.fn(), }; }); return { CognitoIdentityCredentials: mCognitoIdentityCredentials, config: {}, }; }); describe("do I understand mocks", () => { test("let's find out", async () => { console.log(CognitoUser); const mCognitoUser = new CognitoUser(); console.log(mCognitoUser); console.log(mCognitoUser.authenticateUser); await authenticate("foo", "bar"); expect(CognitoUser.authenticate).toBeCalledTimes(1); }); });
错误信息
TypeError: cognitoUser.authenticateUser is not a function
问题分析与解决
1. 核心原因:Cognito相关对象未被正确mock
被测代码中的CognitoUser、AuthenticationDetails如果不是从amazon-cognito-identity-js模块导入,而是使用全局对象,Jest的mock将无法生效,导致getNewCognitoUser创建的是真实的CognitoUser实例(而非mock对象),自然没有你定义的authenticateUser方法。
解决:
在被测代码顶部添加正确的模块导入:
import { CognitoUser, AuthenticationDetails, CognitoUserPool } from 'amazon-cognito-identity-js';
同时确保userPool也是通过mock的构造函数创建:
// 假设appConfig是你的配置模块 import appConfig from './path/to/appConfig'; const userPool = new CognitoUserPool({ UserPoolId: appConfig.UserPoolId, ClientId: appConfig.ClientId });
2. 全局变量导致的意外覆盖
被测代码中cognitoUser = getNewCognitoUser(username);未使用const/let声明,属于全局变量,可能在测试环境中被意外修改,导致指向非mock对象。
解决:
将其改为局部变量:
const cognitoUser = getNewCognitoUser(username);
3. AWS mock的refresh方法未触发Promise resolve
当前AWS mock中refresh只是空函数,未调用传入的回调,会导致setAWSCredentials的Promise一直处于pending状态,进而影响authenticate的执行流程。
解决:
修改AWS mock,让refresh调用回调函数:
jest.mock("aws-sdk", () => { const mCognitoIdentityCredentials = jest.fn().mockImplementation(() => { return { clearCachedId: jest.fn(), refresh: jest.fn((callback) => callback(null)), // 传入null表示认证成功 }; }); return { CognitoIdentityCredentials: mCognitoIdentityCredentials, config: {}, }; });
4. 测试断言错误
原测试中的expect(CognitoUser.authenticate).toBeCalledTimes(1);是错误的,CognitoUser是构造函数,没有authenticate方法,应该断言构造函数调用次数或authenticateUser的调用次数。
修正后的测试断言:
test("let's find out", async () => { const result = await authenticate("foo", "bar"); // 断言CognitoUser构造函数被调用1次 expect(CognitoUser).toBeCalledTimes(1); // 获取mock的CognitoUser实例,断言authenticateUser被调用1次 const mockCognitoUser = CognitoUser.mock.results[0].value; expect(mockCognitoUser.authenticateUser).toBeCalledTimes(1); // 断言认证结果正确 expect(result).toEqual({ idToken: { jwtToken: "fakeToken" } }); });
5. 补充:mock全局配置appConfig
如果appConfig是外部模块,需要同步mock它,避免测试中因配置缺失报错:
jest.mock('../path/to/appConfig', () => ({ UserPoolId: 'fake-user-pool-id', IdentityPoolId: 'fake-identity-pool-id', Logins: { cognito: { identityProviderName: 'fake-provider-name' } } }));
内容的提问来源于stack exchange,提问作者Rob
相关产品推荐
相关产品推荐

