How to mock Axios get response with jest - node.js

How do I mock an axios get response? This test is failing with the following error:
Error: expect(jest.fn()).toHaveReturnedWith(expected)
Expected: {"test": "test"}
Received: {}
Number of returns: 1
This is the test I'm running:
jest.mock('axios');
const axios = require('axios');
describe('GET /searchLocation', () => {
it('should return mock object', () => {
const mockResp = { test: 'test' };
axios.get.mockResolvedValue(mockResp);
axios.get('/');
expect(axios.get).toHaveBeenCalledWith('/'); //passes
expect(axios.get).toHaveReturnedWith(mockResp); //fails
});
});

I think replacing this:
axios.get.mockResolvedValue(mockResp);
With this:
axios.get = jest.fn(() => mockResp);
Should help you.
P.S.: this call expect(axios.get) won't wait for promise to resolve.

Related

mocking a api call not returning data

Trying to mock the following api to return data.
I keep getting the error:
ReferenceError: Cannot access 'mockedApi' before initialization
Mock Code
const mockedApi = jest.fn();
jest.mock("../../../utils/api", () => ({
...jest.requireActual("../../../utils/api"),
get: jest.fn(),
}));
When I wrap it in a function then the response doesn't work.
const mockedApi = jest.fn();
jest.mock("../../../utils/api", () => ({
...jest.requireActual("../../../utils/api"),
get: () => mockedApi,
}));
when I do a log on the api its showing get as a function now that doesn't return anything. when is should be returning data if I was to use. ?
mockedApi.mockResolvedValueOnce({ data: 'hello });
Do I even need to use ...jest.requireActual("../../../utils/api")
I thought this would insure the original methods would not get mocked and only the once I add would be mocked like get. But this doesn't seem to be the case the entire file and all its methods get mocked ?
File been mocked
import axios from "axios";
const api = axios.create({
baseURL: process.env.REACT_APP_SPARQL_URL,
responseType: "json",
});
export const encode = (arr) => {
const urlencoded = new URLSearchParams();
arr.forEach((item) => {
urlencoded.append(...Object.keys(item), ...Object.values(item));
});
return urlencoded;
};
export default api;

Unable to run mockttp with Cypress

I'm trying to run the mockttp with cypress. I used the example that's listed in project github. I changed the port to run on 3000 but I am getting an error saying Cannot add rules before the server is started.
/*global cy:true,before:true*/
/// <reference path="../../node_modules/cypress/types/index.d.ts" />
const superagent = require("superagent");
const mockServer = require("mockttp").getLocal();
describe('mockttp test' , () => {
beforeEach(() => {
mockServer.start(3000);
});
afterEach(() => {
mockServer.stop();
});
it("lets you mock requests, and assert on the results", async () => {
// Mock your endpoints
const mockedPath = mockServer.forGet("/mocked-path");
// ERROR OCCURS HERE
await mockedPath.thenReply(200, "A mocked response");
// Make a request
const response = await superagent.get("http://localhost:3000/mocked-path");
// Assert on the results
expect(response.text).to.equal("A mocked response");
});
});
You need to wait until the server is actually started before running your test, by waiting for the promises returned by mockServer.start() (and by .stop()).
You can either make your beforeEach & afterEach functions async and then await those lines, or you can just add return to return the promise so that Mocha waits for them automatically.

How to mock a function only once in a test suite?

I'm writing integration tests for a project. Within one test suite, I'm invoking a register endpoint in multiple tests. Most of the time I want to test what the actual response of the registerUser function is given certain req parameters.
This all works fine except I also want to test what happens if the registerUser function throws an error. I know I can mock the registerUser function on top of the test suite but this will affect all tests. I've tried to play around with jest.mock and jest.spyOn but I could not get it to work yet.
How can I mock the response of the registerUser function once and restore it afterwards so it doesn't affect the other tests in the suite?
authController.js
router.post('/register', async (req, res) => {
try {
const response = await registerUser(req);
res.status(HttpStatus.OK).json({ response });
} catch (err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ err });
}
});
authController.test.js
const faker = require('faker');
const HttpStatus = require('http-status-codes');
const authService = require('../services/authService');
// -- Tests where the response of the registerUser function are not mocked are here -- //
it('Gives a status code 500 when an unexpected error is thrown', async () => {
const registerUserMock = jest.spyOn(authService, "registerUser");
registerUserMock.mockReturnValue(() => new Error('Oh snap! Something went wrong.'));
const res = await agent.post('/register')
.send({
email: faker.internet.email(),
firstname: faker.name.firstName(),
lastname: faker.name.lastName(),
password: '123',
reTypedPassword: '123',
});
expect(res.statusCode).toBe(HttpStatus.INTERNAL_SERVER_ERROR);
registerUserMock.mockRestore();
});
// -- more tests -- //
Easiest way would be to
group the tests which should use the same mocked response in a suite (describe)
mock the response in that suite's beforeAll hook and save the mock instance
restore the original implementation in that suite's afterAll hook.
describe('tests with successful auth result', () => {
let authSpy;
beforeAll(() => {
authSpy = jest.spyOn(authService, "registerUser").mockReturnValue(...);
});
afterAll(() => {
authSpy.mockRestore();
});
// tests using successful result
});
describe('tests with failing auth result', () => {
// same but with different spy result
});
note two important things:
you need to call mockRestore on the mock instance returned from mockReturnValue, not on the initial spy value
it's best to setup the mock in beforeEach / beforeAll and restore it in afterEach /afterAll, because if you set and restore it directly in the test (it), then if the test fails the spy remains unrestored, and may affect the following tests!

Async Request to API return undefined in Jest

I am quiet new to testing, and specifically to Jest.
I am following several tutorials in which they handle asynchronous code in the manner I am attempting. My code seems to work when I am making a custom Promise that resolves with dummy data. But when I try to use axios to fetch from an external API, Jest gets as a response undefined.
// functions2.js
const axios = require("axios")
const fetch = () => {
axios.get("https://jsonplaceholder.typicode.com/users")
.then(res => res.data)
.catch(err => err);
}
module.exports = fetch;
// functions2.test.js
describe("async operation", ()=>{
it("should be defined", ()=>{
expect(fetch).toBeDefined()
}); // Passed
it("should fetch", async () => {
expect.assertions(1);
const data = await fetch();
expect(data).toBeTruthy();
}) // Did not pass, data is undefined
it("should fetch, using promises", () => {
expect.assertions(1);
return fetch().then(data => {
expect(data).toBeTruthy();
}) // Did not pass, got 0 assertions
})
})
In one tutorial I encountered that this has something to do with Jest running through Node.JS, but I don't know how to handle it because I don't know node.js.
Also, I followed a tutorial by Traversy Media, cloned his Git repo (https://github.com/bradtraversy/jest_testing_basics) and had the same problem (though in the video it worked)
The problem is because you are not returning the promise from fetch.
Update your functions2.js to something like:
const fetch = async () => {
return axios
.get("https://jsonplaceholder.typicode.com/users")
.then(res => res.data)
.catch(err => err);
};

mock nodemailer.createTransport.sendMail with jest

I have some code which uses the nodemailer module.
In the router (router.js), I have
const transporter = nodeMailer.createTransport(emailArgs);
Then inside the route (/login) I have:
...
return transporter.sendMail(mailOptions);
I'm trying to test this route using the jest testing framework. I'm having some trouble mocking out the call to sendMail. I read this nice blogpost about how to use jest mocking, but I'm getting this error:
TypeError: Cannot read property 'sendMail' of undefined
And indeed when I check the value of transporter it's undefined.
Here is my testing code (which doesn't work):
import request from "supertest";
import router from "./router";
jest.mock("nodemailer");
describe("", () => {
...
test("", async () => {
// 1 - 200 status code; 2 - check email was sent
expect.assertions(2);
const response = await request(router)
.post("/login")
// global variable
.send({ "email": email })
.set("Accept", "application/json")
.expect("Content-Type", /json/);
// should complete successfully
expect(response.status).toBe(200);
// TODO not sure how to express the expect statement here
});
});
So my question is how do I mock out a method of an instance of a class which is returned by a module?
I ran into the same problem and found a solution. Here is what I've discovered:
With jest.mock("nodemailer"); you tell jest to replace nodemailer with an auto-mock. This means every property of nodemailer is replaced with an empty mock function (similar to jest.fn()).
That is the reason why you get the error TypeError: Cannot read property 'sendMail' of undefined.
In order to have something useful, you have to define the mock function of nodemailer.createTransport.
In our case we wan't to have an object with a property sendMail. We could do this with nodemailer.createTransport.mockReturnValue({"sendMail": jest.fn()});. Since you may want to test if sendMail was called, it is a good idea to create that mock function before hand.
Here is a complete example of your testing code:
import request from "supertest";
import router from "./router";
const sendMailMock = jest.fn(); // this will return undefined if .sendMail() is called
// In order to return a specific value you can use this instead
// const sendMailMock = jest.fn().mockReturnValue(/* Whatever you would expect as return value */);
jest.mock("nodemailer");
const nodemailer = require("nodemailer"); //doesn't work with import. idk why
nodemailer.createTransport.mockReturnValue({"sendMail": sendMailMock});
beforeEach( () => {
sendMailMock.mockClear();
nodemailer.createTransport.mockClear();
});
describe("", () => {
...
test("", async () => {
// 1 - 200 status code; 2 - check email was sent
expect.assertions(2);
const response = await request(router)
.post("/login")
// global variable
.send({ "email": email })
.set("Accept", "application/json")
.expect("Content-Type", /json/);
// should complete successfully
expect(response.status).toBe(200);
// TODO not sure how to express the expect statement here
expect(sendMailMock).toHaveBeenCalled();
});
});
To mock nodemailer module I do
jest.mock('nodemailer', () => ({
createTransport: jest.fn().mockReturnValue({
sendMail: jest.fn().mockReturnValue((mailoptions, callback) => {})
})
}));
works like a charm
you can also define a mocked function if you need to evaluate .toBeCalledWith() etc:
const sendMailMock = jest.fn()
jest.mock('nodemailer', () => ({
createTransport: jest.fn().mockImplementation(() => ({
sendMail: sendMailMock,
})),
}))
well I still wanted my mailer to work and returning undefined was not working, so I had to change sendMailMock to this:
const sendMailMock = jest.fn((mailOptions, callback) => callback());
This worked for me
Create a mock file at the directory mocks/nodemailer.js (See Jest Manual Mock for reference)
Add the following code to the file. The createTransport method needs to return a response that has a method sendMail for it to work. So see the code used below
class CreateTransportClass {
sendMail(){
//console.log("mocked mailer");
}
}
const createTransport = ()=>{
return new CreateTransportClass()
}
module.exports = {
createTransport
}
In the jest config file (jest.config.js) add the file path to the testPathIgnorePatterns like this:
{
testPathIgnorePatterns: ["/__mocks__/nodemailer.js"],
}
This should work perfectly.

Resources