I am trying to write some unit tests in Jest for a Create React App project I have which uses Amazon Cognito.
The code under test:
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,
});
},
});
});
};
The test
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);
});
});
This tells me that the function authenticateUser is undefined and of course it fails when trying to run
await authenticate("foo", "bar")
stating that
TypeError: cognitoUser.authenticateUser is not a function
AFAICT, the code is properly mocked, so why doesn't it see that there is a mock implementation for authenticateUser?
I found the solution.
The app was generated using Create React App and the default setting for resetMocks is true, which has the effect of blowing away the mock implementations I set up. I am not sure why that's the default, but adding the following to my package.json file solved the problem.
"jest": {
"resetMocks": false
}