I have two identical jest tests, the first one passes and the second fails. From what I have read it could be something to do with mocking the someFunction. I am clearing all the mocks before each test, I have tried jest.resetAllMocks(); but that does not seem to work.
const authenticatedUser = {
"id": 1,
"accessFlags": [],
}
jest.mock("../src/someFunction", () => ({
return {someData: ["a", "b"]}
}));
describe("My Tests", () => {
let ex;
beforeEach(() => {
jest.clearAllMocks();
ex = new FauxExpress();
});
it("Description", async () => {
const req = {
user: { ...authenticatedUser, access: ["admin"] },
};
await doSomethingUsingMockedSomeFunction(req, ex.res);
expect(ex.res.statusCode).toBe(200);
});
it("Description", async () => {
const req = {
user: { ...authenticatedUser, access: ["admin"] },
};
await doSomethingUsingMockedSomeFunction(req, ex.res);
expect(ex.res.statusCode).toBe(200);
});
});
Related
This is my helper function in src/utils/calls/aws.js
export const loginCognito = (cognitoUser, authenticationDetails) => {
return new Promise((resolve, reject) => {
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: resolve,
onFailure: reject,
newPasswordRequired: resolve,
});
});
};
This is my test file:
import { fireEvent, screen, waitFor } from "#testing-library/react";
import { LoginPage } from "../../../views/LoginPage/LoginPage";
import { renderWithProviders } from "../../__test-utils__/test-utils";
// eslint-disable-next-line jest/no-mocks-import
import { LOCALSTORAGE_ACCESS_TOKEN } from "../../../utils/constants/constant";
const accessToken = "an.access.token";
const promisedResult = {
"idToken": {
"jwtToken": "an.access.token",
"payload": {
"sub": "354548-5454-c59637523bfd",
"email_verified": true,
"iss": "https://cognito-idp.eu-central-1.amazonaws.com/eu-central-1_tomahwaf",
"cognito:username": "john-doe",
"preferred_username": "john-doe",
"aud": "client.id.aws",
"custom:dataInizioContratto": "01/01/2020",
"event_id": "ee5626d5-ffc2-4ecf-89df-2e1a83c0c174",
"token_use": "id",
"auth_time": 1670256322,
"custom:azienda": "acme",
"exp": 1670259922,
"iat": 1670256322,
"email": "info#example.com"
}
},
"refreshToken": {
"token": "a.refresh.token"
},
"accessToken": {
"jwtToken": "an.access.token",
"payload": {
"sub": "354548-5454-c59637523bfd",
"event_id": "ee5626d5-ffc2-4ecf-89df-2e1a83c0c174",
"token_use": "access",
"scope": "aws.cognito.signin.user.admin",
"auth_time": 1670256322,
"iss": "https://cognito-idp.eu-central-1.amazonaws.com/eu-central-1_tomahwaf",
"exp": 1670259922,
"iat": 1670256322,
"jti": "083d4b9a-5042-485b-bb65-c66e8ae5b921",
"client_id": "client.id.aws",
"username": "john-doe"
}
},
"clockDrift": 0
};
jest.mock("../../../utils/calls/aws.js", () => ({
//loginCognito: jest.fn().mockReturnValue(Promise.resolve({accessToken: "an.access.token"}))
//loginCognito: jest.fn(() => Promise.resolve(promisedResult))
//loginCognito: jest.fn(() => promisedResult)
loginCognito: jest.fn().mockResolvedValue(promisedResult)
}));
describe("Test LoginPage", () => {
it("Can press button without userEvent", async () => {
renderWithProviders(<LoginPage />);
const inputUsername = screen.getByLabelText(/username/i);
fireEvent.change(inputUsername, {
target: {
value: "info#example.com",
},
});
const inputPassword = screen.getByLabelText(/password/i);
fireEvent.change(inputPassword, {
target: {
value: "thisIsASecretPassword",
},
});
const loginButton = screen.getByRole("button", { name: /login/i });
fireEvent.click(loginButton);
await waitFor(() => {
expect(localStorage.getItem(LOCALSTORAGE_ACCESS_TOKEN)).toEqual(
accessToken
);
});
});
});
In the real file (LoginPage) I have
const handleClick = async () => {
// Fetch the data from cognito
const {cognitoUser, authenticationDetails} = loginCognitoUser(currentItem);
// Call AWS
try {
const isLogged = await loginCognito(cognitoUser, authenticationDetails);
console.log(">>>>>>>>>>>>>>>>>>>", JSON.stringify(isLogged))
} catch (e) {
console.log(e)
}
};
For real implementation it works, but when I call from the test I get
console.log
>>>>>>>>>>>>>>>>>>> undefined
at handleClick (src/views/LoginPage/LoginPage.js:52:15)
Doesn't mock of a function "bypasses" it returning what I want?
I have made 4 tests, I don't know anymore what to do:
//loginCognito: jest.fn().mockReturnValue(Promise.resolve({accessToken: "an.access.token"}))
//loginCognito: jest.fn(() => Promise.resolve(promisedResult))
//loginCognito: jest.fn(() => promisedResult)
loginCognito: jest.fn().mockResolvedValue(promisedResult)
I tried add a console.log inside my aws function and... If I remove the mocked I have the console.log, so I think that function is correctly mocked but cannot have the return.
Edit nr. 2
I tried another thing:
export const loginCognito = () => {
return "ciao"
}
And in test:
jest.mock("../../../utils/calls/aws.js", () => ({
loginCognito: jest.fn().mockReturnValue("addio")
}));
The page under test has console.log undefined!
try {
//const isLogged = await loginCognito(cognitoUser, authenticationDetails);
const isLogged = loginCognito();
console.log("==========", isLogged)
} catch (e) {
console.log(e)
}
Am I that I don't know how mock it works?
Console
console.log
========== undefined
at handleClick (src/views/LoginPage/LoginPage.js:55:15)
I need to bypass totally the AWS funciton...
I can't really speak for why your code is not working, but this is how I would go about writing your test:
// import the function you want to mock
import { loginCognito } from "../../../utils/calls/aws.js";
// tell jest to mock your module
jest.mock("../../../utils/calls/aws.js");
// before each test, mock the implementation
beforeEach(() => {
loginCognito.mockResolvedValue(...);
});
// cleanup after each test
afterEach(() => {
loginCognito.mockClear(...);
});
If you're using typescript, you will need to make typescript aware that the method is mocked. This is done by casting the method, which is mostly a tedium required for typescript. As a matter of convention, I usually put the work "mock" in front of the name:
const mockLoginCongnito = loginCognito as jest.MockedFunction<typeof loginCognito>;
I'm trying to test a service in Nestjs which is responsible for getting one record out of a mongo database, using Jest.
As per common convention, when writing unit tests that test services we can mock a record that would sit in a database.
I'm trying the following implementation:
import { Test } from '#nestjs/testing';
import { QuestionsService } from './questions.service';
import { CreateQuestionRequestDto } from './dto/create-question-request.dto';
import { getModelToken } from '#nestjs/mongoose';
import { UpdateQuestionRequestDto } from './dto/update-question-request.dto';
import { NotFoundException } from '#nestjs/common';
import { DuplicateQuestionRequestDto } from './dto/duplicate-question-request.dto';
const testQuestion: CreateQuestionRequestDto = {
data: {
createdBy: { id: 0, name: '' },
lanugageTexts: undefined,
options: undefined,
status: undefined,
type: undefined,
entityId: 1,
propertyId: 'propHash1',
companyId: 1,
entityType: 'announcement',
},
};
describe('QuestionsService', () => {
let questionService: QuestionsService;
let findOne: jest.Mock;
let findOneAndUpdate: jest.Mock;
let find: jest.Mock;
beforeEach(async () => {
// save = jest.fn();
findOne = jest.fn();
findOneAndUpdate = jest.fn();
find = jest.fn();
const module = await Test.createTestingModule({
providers: [
QuestionsService,
{
provide: getModelToken('Question'),
useValue: {}
}
]
})
.compile();
questionService = await module.get<QuestionsService>(QuestionsService);
});
it('should be defined', () => {
expect(questionService).toBeDefined();
});
/**
* Question Get
*/
describe('when getting a question', () => {
describe('and the questionId does not exist', () => {
beforeEach(() => {
findOne.mockReturnValue(undefined);
})
it('should throw a NotFound exception', async () => {
const response = await questionService.get('announcement', 9136500000);
expect(response).toThrow(NotFoundException);
});
});
describe('and the questionId exists', () => {
beforeEach(() => {
findOne.mockResolvedValue(Promise.resolve(testQuestion));
});
it('should update the correct question', async() => {
const response = await questionService.get('announcement', 1);
expect(response).toMatchObject(updatedTestQuestion);
});
});
});
});
When I run this test I get the following error message.
● QuestionsService › when getting a question › and the questionId does not exist › should throw a NotFound exception
TypeError: this.questionModel.find is not a function
52 | const data: Question[] = [];
53 | const questions = await this.questionModel
> 54 | .find(
| ^
55 | { entityType: entityType, entityId: entityId, status: QuestionStatus.ACTIVE },
56 | { answers: 0 },
57 | {
at QuestionsService.get (questions/questions.service.ts:54:14)
at Object.<anonymous> (questions/questions.spec.ts:128:56)
The service method I'm testing is.
async get(entityType: string, entityId: number): Promise<any> {
const data: Question[] = [];
const questions = await this.questionModel
.find(
{ entityType: entityType, entityId: entityId, status: QuestionStatus.ACTIVE },
{ answers: 0 },
{
sort: { _id: -1 },
limit: 1,
}
)
.exec();
if (!questions.length) {
throw new NotFoundException();
}
questions.forEach((question) => {
data.push(question);
});
return { data };
}
find() is the mongoose method that fetches the record from the database. I believe for the test I need to somehow include these methods I'm using in the service and mock them but I cannot find one clear answer.
In my calendar.spec.js, I have:
const { google } = require('googleapis')
const googleCalendar = google.calendar('v3')
...
before(() => {
sinon.stub(googleCalendar.calendarList, 'list').resolves({ data: true })
})
after(() => {
googleCalendar.calendarList.list.restore()
})
In my calendar.js, I have:
const { google } = require('googleapis')
const googleCalendar = google.calendar('v3')
let { data } = await googleCalendar.calendarList.list({
auth: oauth2Client
})
But it doesn't appear to be stubbed. It goes ahead and tries to connect to Google Calendar. What am I doing wrong?
You can mock the entire googleapis module with mock-require.
const mock = require('mock-require');
mock('googleapis', {
google: {
calendar: () => ({
calendarList: {
list: () => {
return Promise.resolve({
data: {
foo: 'bar'
}
});
}
}
})
}
});
Once you mocked it, your module will consume the mocked module instead of the original so you can test it. So if you module is exposing a method that calls the API, something like that:
exports.init = async () => {
const { google } = require('googleapis');
const googleCalendar = google.calendar('v3');
let { data } = await googleCalendar.calendarList.list({
auth: 'auth'
});
return data;
}
The test will be
describe('test', () => {
it('should call the api and console the output', async () => {
const result = await init();
assert.isTrue(result.foo === 'bar');
});
});
Here is a small repo to play with it: https://github.com/moshfeu/mock-google-apis
This is the code to test:
const AWS = require('aws-sdk');
const { APPLICATIONS, NOTIFICATION_FREQUENCIES } = require('./config');
exports.createHandler = ({ notificationService }) => async (event, context) => {
try{
Object.values(APPLICATIONS).forEach(async appId => {
const notifications = await notificationService
.getNotificationsByApplication(appId);
const dailyNotifications =notifications.filter(
e =>
e.frequency === NOTIFICATION_FREQUENCIES.DAILY,
);
console.log('dailyNo', dailyNotifications);
const dailyTemplate = notificationService.prepareDailyTemplate(
dailyNotifications
);
console.log('dailyTemplate', dailyTemplate);
notificationService.notifyToAdmin(dailyTemplate);
});
}
catch(err) {
console.log(err);
}
};
And this is my test using sinon:
const sinon = require('sinon');
const { APPLICATIONS, NOTIFICATION_FREQUENCIES } = require('../lib/config');
describe('Daily notifier tests', () => {
it('should prepare daily template for each of the applications', () => {
const notificationService = require('../lib/notificationService').createHandler({
commands: {},
simpleMailService: {},
});
const notifications = [
{
type: 'create_order',
frequency: NOTIFICATION_FREQUENCIES.DAILY,
},
{
type: 'create_order',
frequency: NOTIFICATION_FREQUENCIES.DAILY,
},
{
type: 'create_order',
frequency: NOTIFICATION_FREQUENCIES.MONTHLY,
},
];
const template = 'some html template as string';
sinon.stub(notificationService, 'getNotificationsByApplication').resolves(notifications);
sinon.stub(notificationService, 'prepareDailyTemplate').returns(template);
sinon.stub(notificationService, 'notifyToAdmin');
const sut = require('../lib/dailyNotifier').createHandler({
notificationService,
});
const event = {};
const context = {};
sut(event, context);
const dailyNotifications = [
{
type: 'create_order',
frequency: NOTIFICATION_FREQUENCIES.DAILY,
},
{
type: 'create_order',
frequency: NOTIFICATION_FREQUENCIES.DAILY,
}
];
sinon.assert.calledOnce(notificationService.prepareDailyTemplate);
sinon.assert.calledWith(notificationService.notifyToAdmin, template);
});
});
According to sinon the method prepareDailyTemplate is not called at all (0 times), but when I execute the test I can even see the console.log 'dailyTemplate', which means that the method has been executed once.
The error message:
AssertError: expected prepareDailyTemplate to be called once but was called 0 times
What I am doing wrong?
sut is an async function created by createHandler so it returns a Promise.
You just need to await the Promise that it returns:
it('should prepare daily template for each of the applications', async () => { // <= async
// ...
await sut(event, context); // <= await
// ...
sinon.assert.calledOnce(notificationService.prepareDailyTemplate); // Success!
});
I'm learning double tests with a simple example:
const Database = require('./Database')
const setupNewUser = (info, callback) => {
const user = {
name: info.name,
nameLowercase: info.name.toLowerCase()
}
try {
Database.save(user, callback)
} catch (err) {
callback(err)
}
}
module.exports = setupNewUser
I have a function that takes an object and a callback:
const Database = {
save: (user, callback) => {
callback(user)
}
}
module.exports = Database
How can test that save is called with both an object and a callback. Below is what I'm trying:
it('it calls Database.save with a callback', () => {
const saveSpy = sinon.spy(Database, 'save')
const arg = {
name: info.name,
nameLowercase: info.name.toLowerCase()
}
setupNewUser(info, function() {})
//I'm able to assert that save is called with arg sinon.assert.calledWith(saveSpy, arg)
sinon.assert.calledWith(saveSpy, arg, function() {}) //This is failing
})
You should .stub your API calls vs .spy. I would also recommend using sinon.sandbox so your test cleanup is easy to manage. Read about it here
describe('Setup new user', function() {
const sandbox = sinon.createSandbox();
afterEach(function() {
sandbox.restore();
});
it('should call Database.save with a callback', function(){
const databaseSaveStub = sandbox.stub(Database, 'save');
const user = {
name: 'Some Name',
nameLowercase: 'some name'
};
const callbackFn = sandbox.spy();
setupNewUser(user, callbackFn);
sinon.assert.calledOnce(Database.save);
sinon.assert.calledWith(databaseSaveStub, user, callbackFn);
});
});