How to mock a method inside Express router Jest test? - javascript

I'm trying to test a router in Node.js app with Jest + Supertest, but my router is making a call to service, which is calling the endpoint:
router.post('/login', async (req, res, next) => {
try {
const { username, password } = req.body;
// I WANT TO MOCK userService.getUserInfo FUNCTION, BECAUSE IT IS MAKING A POST CALL
const identity = await userService.getUserInfo(username, password);
if (!identity.authenticated) {
return res.json({});
}
const requiredTenantId = process.env.TENANT_ID;
const tenant = identity.tenants.find(it => it.id === requiredTenantId);
if (requiredTenantId && !tenant) {
return res.json({});
}
const userResponse = {
...identity,
token: jwt.sign(identity, envVars.getVar(envVars.variables.AUTH_TOKEN_SECRET), {
expiresIn: '2h',
}),
};
return res.json(userResponse);
} catch (err) {
return next(err);
}
});
This is my test that works well:
test('Authorized - respond with user object', async () => {
const response = await request(app)
.post('/api/user/login')
.send(users.authorized);
expect(response.body).toHaveProperty('authenticated', true);
});
this is how getUserInfo function looks like:
const getUserInfo = async (username, password) => {
const identity = await axios.post('/user', {username, password});
return identity;
}
but it executes the method getUserInfo inside a router and this method is making a REST call - I want to mock this method in order to avoid REST calls to other services.
How it could be done?
I've found a mockImplementation function in Jest docs https://jestjs.io/docs/en/mock-function-api.html#mockfnmockimplementationfn
but how I can mock func inside a supertest testing?

You can use jest's auto mocking at the top of your test
like so:
jest.mock('./path/to/userService');
// and include it as well in your test
const userService = require('./path/to/userService');
it will generate a mock of the entire module and every function will be replaced with jest.fn() with no implementation
and then depending on the userService if it's just an object it's getUserInfo method will be a jest.fn() and you can set it's return value like this:
// resolved value as it should return a promise
userService.getUserInfo.mockResolvedValue(mockIdentity);
and the mockIdentity will have to look something like this:
const mockIdentity = {
authenticated: true,
tenants: [
{
id: "x12",
mockInfo: "mock-info-value"
}
],
mother: "Superwoman",
father: "Superman"
})
}

Related

how do i call a method from another controller node

I have 2 models and 2 controllers.
UserModel.js, UserNameModel.js, and userController.js, userNameController.js
After I successfully register a user, I want to record & store that new username. the goal is to not reuse an already-used username
How do I design this function without calling a controller in the other controller,
userController.js
const register = async (req, res) => {
const { userName, email } = req.body
const newUser = await createNewUser(name, email)
res.status(StatusCodes.OK).json({ newUser })
}
userNameController.js
......
The module.exports is a special object which is included in every JavaScript file in the Node.js application by default. The module is a variable that represents the current module, and exports is an object that will be exposed as a module. So, whatever you assign to module.exports will be exposed as a module.
so, you can use the separator file to write all functions you need for each model like users, contacts, and messages....
users.js
module.exports.createNewUser = async (data) => {
write your code here.
return user;
};
module.exports.checkUserExist = async (email) => {
write your code here.
return user;
};
module.exports.deleteUser = async (userId) => {
write your code here.
return true;
};
and if you want to use the file on any controller, should call it like.
userController.js
const {createNewUser, checkUserExist, deleteUser} = require(../users.js);
router.get('create-user', async (req, res) => {
const newUser = await createNewUser(req.body);
res.status(200).json({ newUser });
}

How to change the return value of a method within a mocked dependency in Jest?

I am writing a test that looks like this:
import { getAllUsers } from "./users";
import { getMockReq, getMockRes } from "#jest-mock/express";
import User from "../../models/User";
jest.mock("../../models/User", () => ({
find: jest.fn(), // I want to change the return value of this mock in each test.
}));
describe("getAllUsers", () => {
test("makes request to database", async () => {
const req = getMockReq();
const { res, next, clearMockRes } = getMockRes();
await getAllUsers(req, res, next);
expect(User.find).toHaveBeenCalledTimes(1);
expect(User.find).toHaveBeenCalledWith();
});
});
Within the jest.mock statement, I am creating a mock of the imported 'User' dependency, specifically for the User.find() method. What I would like to do is set the return value of the User.find() method within each test that I write. Is this possible?
This SO question is similar, but my problem is that I can't import the 'find' method individually, it only comes packaged within the User dependency.
Well after much trial and error, here is a working solution:
import { getAllUsers } from "./users";
import { getMockReq, getMockRes } from "#jest-mock/express";
import User from "../../models/User";
const UserFindMock = jest.spyOn(User, "find");
const UserFind = jest.fn();
UserFindMock.mockImplementation(UserFind);
describe("getAllUsers", () => {
test("makes request to database", async () => {
UserFind.mockReturnValue(["buster"]);
const req = getMockReq();
const { res, next, clearMockRes } = getMockRes();
await getAllUsers(req, res, next);
expect(User.find).toHaveBeenCalledTimes(1);
expect(User.find).toHaveBeenCalledWith();
expect(res.send).toHaveBeenCalledWith(["buster"]);
});
});
Note how I've used jest.spyOn to set a jest.fn() on the specific User find method, and I can use the UserFind variable to set the return value of the mock implementation.

How to use Node.js express async callback response with a Service

I'm new to node and trying to understand how to use async call inside an other async function, here's an example.
My routing:
/**
* PAGE: PROFILE EXPOSITIONS
*/
exports.expositions = async(function* (req, res) {
respond(res, 'users/profile/exhibitors', ProfileResponse.expositions( req ));
});
Here's what ProfileResponse.expositions look like
exports.expositions = function*(req) {
return {
title: "Expositions",
user: req.user,
path: "/profile",
waiting_exposition_requests: yield UserService.getWaitingExpositionRequest(req.user.id)
};
};
I'm trying to use getWaitingExpositionRequest which looks like this
exports.getWaitingExpositionRequest = function*(userId) {
let currentUser = User.find(userId);
let waiting_exposition_requests = [];
currentUser.exposition_requests.forEach(function(exposition_request) {
if(exposition_request.status === "waiting_for_exhibitor") {
waiting_exposition_requests.push(exposition_request);
}
});
return waiting_exposition_requests;
};
But in the console I get a Promise error, any idea how I can use this async function returned? I'm sure it's a syntax error but I'm a little lost with the async stuff.

How do I test axios in Jest?

I have this action in React:
export function fetchPosts() {
const request = axios.get(`${WORDPRESS_URL}`);
return {
type: FETCH_POSTS,
payload: request
}
}
How do I test Axios in this case?
Jest has this use case on their site for asynchronous code where they use a mock function, but can I do this with Axios?
Reference: An Async Example
I have done this so far to test that it is returning the correct type:
it('should dispatch actions with the correct type', () => {
store.dispatch(fetchPosts());
let action = store.getActions();
expect(action[0].type).toBe(FETCH_POSTS);
});
How can I pass in mock data and test that it returns?
Without using any other libraries:
import * as axios from "axios";
// Mock out all top level functions, such as get, put, delete and post:
jest.mock("axios");
// ...
test("good response", () => {
axios.get.mockImplementation(() => Promise.resolve({ data: {...} }));
// ...
});
test("bad response", () => {
axios.get.mockImplementation(() => Promise.reject({ ... }));
// ...
});
It is possible to specify the response code:
axios.get.mockImplementation(() => Promise.resolve({ status: 200, data: {...} }));
It is possible to change the mock based on the parameters:
axios.get.mockImplementation((url) => {
if (url === 'www.example.com') {
return Promise.resolve({ data: {...} });
} else {
//...
}
});
Jest v23 introduced some syntactic sugar for mocking Promises:
axios.get.mockImplementation(() => Promise.resolve({ data: {...} }));
It can be simplified to
axios.get.mockResolvedValue({ data: {...} });
There is also an equivalent for rejected promises: mockRejectedValue.
Further Reading:
Jest mocking documentation
A GitHub discussion that explains about the scope of the jest.mock("axios") line.
A related question which addresses applying the techniques above to Axios request interceptors.
Using jest functions like mockImplementation in TypeScript: Typescript and Jest: Avoiding type errors on mocked functions
I used axios-mock-adapter.
In this case the service is described in ./chatbot.
In the mock adapter you specify what to return when the API endpoint is consumed.
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import chatbot from './chatbot';
describe('Chatbot', () => {
it('returns data when sendMessage is called', done => {
var mock = new MockAdapter(axios);
const data = { response: true };
mock.onGet('https://us-central1-hutoma-backend.cloudfunctions.net/chat').reply(200, data);
chatbot.sendMessage(0, 'any').then(response => {
expect(response).toEqual(data);
done();
});
});
});
You can see it the whole example here:
Service:
https://github.com/lnolazco/hutoma-test/blob/master/src/services/chatbot.js
Test:
https://github.com/lnolazco/hutoma-test/blob/master/src/services/chatbot.test.js
I could do that following the steps:
Create a folder __mocks__/ (as pointed by #Januartha comment)
Implement an axios.js mock file
Use my implemented module on test
The mock will happen automatically
Example of the mock module:
module.exports = {
get: jest.fn((url) => {
if (url === '/something') {
return Promise.resolve({
data: 'data'
});
}
}),
post: jest.fn((url) => {
if (url === '/something') {
return Promise.resolve({
data: 'data'
});
}
if (url === '/something2') {
return Promise.resolve({
data: 'data2'
});
}
}),
create: jest.fn(function () {
return this;
})
};
Look at this
The function to test album.js
const fetchAlbum = function () {
return axios
.get("https://jsonplaceholder.typicode.com/albums/2")
.then((response) => {
return response.data;
});
};
The test album.test.js
const axios = require("axios");
const { fetchAlbum } = require("../utils.js");
jest.mock("axios");
test("mock axios get function", async () => {
expect.assertions(1);
const album = {
userId: 1,
id: 2,
title: "sunt qui excepturi placeat culpa",
};
const payload = { data: album };
// Now mock axios get method
axios.get = jest.fn().mockResolvedValue(payload);
await expect(fetchAlbum()).resolves.toEqual(album);
});
I've done this with nock, like so:
import nock from 'nock'
import axios from 'axios'
import httpAdapter from 'axios/lib/adapters/http'
axios.defaults.adapter = httpAdapter
describe('foo', () => {
it('bar', () => {
nock('https://example.com:443')
.get('/example')
.reply(200, 'some payload')
// test...
})
})
For those looking to use axios-mock-adapter in place of the mockfetch example in the Redux documentation for async testing, I successfully used the following:
File actions.test.js:
describe('SignInUser', () => {
var history = {
push: function(str) {
expect(str).toEqual('/feed');
}
}
it('Dispatches authorization', () => {
let mock = new MockAdapter(axios);
mock.onPost(`${ROOT_URL}/auth/signin`, {
email: 'test#test.com',
password: 'test'
}).reply(200, {token: 'testToken' });
const expectedActions = [ { type: types.AUTH_USER } ];
const store = mockStore({ auth: [] });
return store.dispatch(actions.signInUser({
email: 'test#test.com',
password: 'test',
}, history)).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
In order to test a successful case for signInUser in file actions/index.js:
export const signInUser = ({ email, password }, history) => async dispatch => {
const res = await axios.post(`${ROOT_URL}/auth/signin`, { email, password })
.catch(({ response: { data } }) => {
...
});
if (res) {
dispatch({ type: AUTH_USER }); // Test verified this
localStorage.setItem('token', res.data.token); // Test mocked this
history.push('/feed'); // Test mocked this
}
}
Given that this is being done with jest, the localstorage call had to be mocked. This was in file src/setupTests.js:
const localStorageMock = {
removeItem: jest.fn(),
getItem: jest.fn(),
setItem: jest.fn(),
clear: jest.fn()
};
global.localStorage = localStorageMock;
New tools for testing have been introduced since the question was initially answered.
The problem with mocking is that you often test the mock and not the real context of your code, leaving some areas of this context untested.
An improvement over telling axios what promise to return is intercepting http requests via Service Workers.
Service worker is a client-side programmable proxy between your web app and the outside world. So instead of mocking promise resolution it is a more broader solution to mock the proxy server itself, intercepting requests to be tested. Since the interception happens on the network level, your application knows nothing about the mocking.
You can use msw (Mock Service Worker) library to do just that. Here is a short video explaining how it works.
The most basic setup I can think of is this:
1️⃣ set up handlers, which are similar to express.js routing methods;
2️⃣ set up mock server and pass handlers as it’s arguments;
3️⃣ configure tests to so that mock server will intercept our requests;
4️⃣ perform tests;
5️⃣ close mock server.
Say you want to test the following feature:
import axios from "axios";
export const fetchPosts = async () => {
const request = await axios.get("/some/endpoint/");
return {
payload: request,
};
};
Then test could look like this:
import { rest } from "msw";
import { setupServer } from "msw/node";
import fetchPosts from "./somewhere";
// handlers are usually saved in separate file(s) in one destined place of the app,
// so that you don't have to search for them when the endpoints have changed
const handlers = [ 1️⃣
rest.get("/some/endpoint/", (req, res, ctx) =>
res(ctx.json({ message: "success" }))
),
];
const server = setupServer(...handlers); 2️⃣
beforeAll(() => {
server.listen(); 3️⃣
});
describe("fetchPosts", () => {
it("should return 'success' message", async () => {
const resp = await fetchPosts();
expect(resp.payload?.data?.message).toEqual("success"); 4️⃣
});
});
afterAll(() => {
server.close(); 5️⃣
});
The configuration may be different depending on framework you are using. Some general examples for, among others, React (both REST and GraphQL) and Angular can be found on MSW’ repo. A Vue example is provided by VueMastery.
You can also find examples on MSW' recipes page.

Mock a method of a service called by the tested one when using Jest

I am trying to mock a method's service i export as a module from my test.
This is something i use to do with "sinon", but i would like to use jest as much as possible.
This is a classic test, i have an "authentication" service and a "mailer" service.
The "authentication" service can register new users, and after each new registration, it ask the mailer service to send the new user a "welcome email".
So testing the register method of my authentication service, i would like to assert (and mock) the "send" method of the mailer service.
How to do that? Here is what i tried, but it calls the original mailer.send method:
// authentication.js
const mailer = require('./mailer');
class authentication {
register() { // The method i am trying to test
// ...
mailer.send();
}
}
const authentication = new Authentication();
module.exports = authentication;
// mailer.js
class Mailer {
send() { // The method i am trying to mock
// ...
}
}
const mailer = new Mailer();
module.exports = mailer;
// authentication.test.js
const authentication = require('../../services/authentication');
describe('Service Authentication', () => {
describe('register', () => {
test('should send a welcome email', done => {
co(function* () {
try {
jest.mock('../../services/mailer');
const mailer = require('../../services/mailer');
mailer.send = jest.fn( () => { // I would like this mock to be called in authentication.register()
console.log('SEND MOCK CALLED !');
return Promise.resolve();
});
yield authentication.register(knownUser);
// expect();
done();
} catch(e) {
done(e);
}
});
});
});
});
First you have to mock the mailer module with a spy so you can later set. And you to let jest know about using a promise in your test, have a look at the docs for the two ways to do this.
const authentication = require('../../services/authentication');
const mailer = require('../../services/mailer');
jest.mock('../../services/mailer', () => ({send: jest.fn()}));
describe('Service Authentication', () => {
describe('register', () => {
test('should send a welcome email', async() => {
const p = Promise.resolve()
mailer.send.mockImplementation(() => p)
authentication.register(knownUser);
await p
expect(mailer.send).toHaveBeenCalled;
}
});
});
});
});

Categories

Resources