mocking a api call not returning data - jestjs

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;

Related

Axios Problems (uncaught in promise) using zustand

Hi there i have problems with making the api call to my server. Using Postman i receive correctly my data from the URL but when i come into react and i use Axios to get those data i have the Axios error Uncaught in promise status code: 300 Here my code.
import {create} from 'zustand'
import axios from 'axios'
const URL1 = "http://localhost:5000/livespa"
export const useStore = create((set, get) => ({
liveSpa:[],
loadLivespa: async () => {
const response = await axios.get(URL1)
set({ liveSpa: await response.data})
},
}))
And my frontend page is like
const LiveSpa = () => {
const data = useStore((state) => state.liveSpa)
const loadBets = useStore((state)=> state.loadLivespa)
useEffect(() => {
loadBets()
}, [])
return (...)
}
I tried using Fetch but same problems, debugging my node server but it works fine using Postman

Express Jest and Supertest how to mock middleware with argument

I have been trying for several hours to test an endpoint of my rest api with Jest, Supertest and Express.
This endpoint is protected by an authentication middleware named "advancedAuthGuard".
So I'm trying to mock this middleware in order to skip authentication check for endpoints testing
//./router.js
router.get('/route1', advancedAuthGuard(false), controller);
Important: advancedAuthGuard is a middleware that accepts configuration argument ( curried middleware )
//./middleware/advancedAuthGuard.js
const advancedAuthGuard = (additionalCondition) => (req,res,next) => {
//Check authentication logic ...
const isAuth = true
if (isAuth && !additionalCondition)
next()
else
next(new Error('Please auth'))
}
When I run the following test to check if I get status code '200' . The test fail before run.
//./test.js
import supertest from "supertest"
import app from './app.js'
import { advancedAuthGuard } from "./middlewares/advancedAuthGuard";
jest.mock("./middlewares/advancedAuthGuard")
const request = supertest(app)
beforeEach(()=>{
jest.clearAllMocks()
})
it("should '/route1' respond with 200 status", async ()=>{
const mockedMiddleware = jest.fn().mockImplementation((res, req, next)=> next() )
advancedAuthGuard.mockReturnValue(mockedMiddleware)
const res = await request.get("/route1")
expect(res.status).toEqual(200)
})
I get the following error:
Test suite failed to run
Route.get() requires a callback function but got a [object Undefined]
> 10 | router.get('/route1', advancedAuthGuard(false), controller);
| ^
I therefore deduce that the problem comes from the mock...
And when I debug it via console.log, I realize that I get an incomplete mock :
I was able to verify that the mock of function (advancedAuthGuard(false)) was present
But this mock should return a second mock of type function (req,res,next){}, however it only returns undefined
I also noticed that:
This mock issue only occurs with curried middlewares (middleware with input parameters)
This mock issue only occurs when the curried middleware is executed in express router. (When I tried the mock outside express router, the mock appears correctly)
So I would like to know why it is impossible for me to mock a curried middleware ( middleware with argument ) used in an Expressjs endpoint with Jest and Supertest.
Here is a github link with minimal express, jest, supertest config, where you can reproduce this problem, by running the test.js file. https://github.com/enzo-cora/jest-mock-middleware
Not sure why it is not working the way you tried but if you pass the mock implementation to the autoMock function it will do the trick.
import supertest from "supertest";
import app from "./app";
jest.mock("./middlewares/simpleAuthGuard", () => ({
simpleAuthGuard: (_, __, next) => next()
}));
jest.mock("./middlewares/advancedAuthGuard", () => ({
advancedAuthGuard: () => (_, __, next) => next()
}));
const request = supertest(app);
describe('My tests', () => {
beforeEach(() => jest.clearAllMocks());
it("should '/route1' with advanced middleware work", async () => {
const res = await request.get("/route1");
expect(res.status).toEqual(200);
});
it("should '/route2' with simple middleware work", async () => {
const res = await request.get("/route2")
expect(res.status).toEqual(200)
});
});

Why is axios cookie undefined?

When I try to hit an endpoint with postman everything works, so I assume the problem is probably with my axios request as when logging req.headers.cookies on server after performing this axios request the value is undefined.
Cookies in browser work as well they are set correctly.
When i performed this request in postman the value of req.headers.cookie was fine and the request has been performed without any errors.
Client code:
useEffect(() => {
(async () => {
const res = await axios.post('http://localhost:4000/refresh_token', {
withCredentials: true,
});
})();
}, []);
Server code (endpoint function):
export const validateRefreshToken = async (req, res) => {
console.log(req.headers.cookie); // undefined
const { token } = parse(req.headers.cookie);
...
};
Error message: TypeError argument str must be a string.
This error points to the parse function.
Has anyone experienced this before? Any ides on how I can fix this issue?
With Axios POST, 1st arg is the url, 2nd arg is data and the 3rd arg is for options.
Provide withCredentials: true in the 3rd argument of Axios.post
useEffect(() => {
(async () => {
const res = await axios.post('http://localhost:4000/refresh_token', {} ,{
withCredentials: true,
});
})();
}, []);

Issues mocking return value of Axios post call for unit test

Trying to mock an Axios call to unit test a token response from our identity software. Axios is not getting called at all and because of that my return is always undefined.
I've tried changing up Axios call to axios.post and changing the way I've mocked this more times then I can count. I don't believe like I should have to install another mocking framework just for Axios to mock this one function.
Implementation:
async getAuthToken() {
const oauthUrl = process.env.OAUTHURL;
const oauthAudience = process.env.OAUTHAudience;
const oauthUsername = process.env.OAUTHUSERNAME;
const oauthPassword = process.env.OAUTHPASSWORD;
let urlForAuth = oauthUrl
urlForAuth = urlForAuth + '/as/token.oauth2?';
urlForAuth = urlForAuth + 'grant_type=client_credentials';
urlForAuth = urlForAuth + '&aud=' + oauthAudience + '/';
urlForAuth = urlForAuth + '&scope=' + oauthAudience + '/.read';
const options = {
method: 'POST',
url: urlForAuth,
headers: {
'Authorization': "Basic " + Buffer.from(oauthUsername + ":" + oauthPassword).toString("base64")
},
responseType: 'json'
};
try{
let response = await axios(options);
return response.data.access_token;
}
catch(e){
console.log(e);
throw e;
}
}
Test Case:
test('token Is Returned', async () => {
expect.hasAssertions();
let Response = `{
"access_token": "thisisatoken",
"token_type": "Bearer",
"expires_in": 3599
}`;
axios = jest.fn(() => Promise.resolve());
axios.mockImplementationOnce(() =>
Promise.resolve({
data: Response
})
);
let response = await AuthService.getAuthToken();
expect(axios).toHaveBeenCalledTimes(1);
expect(response).toEqual("thisisatoken");
});
The error I am getting is
Expected mock function to have been called one time, but it was called zero times.
When I debug the data element on the response contains the following:
data:"Copyright (c) 2019 Next Small Things\n"
That is no where in my code. help.
You cannot mock things this way. Actually you are mocking axios only for your test's code but not for component under test that import's axios on its own.
You need to mock module properly and you have actually plenty of options:
provide ready-to-use mock in __mocks__ folder
call jest.mock('axios') to get autogenerated mock(each exported member will become jest.fn automatically)
provide factory for mock jest.mock('axios', () => { .... return object like it all are exported from file ... })
Also you need to import axios into your test to access it:
import axios from 'axios';
jest.mock('axios');
test('token Is Returned', async () => {
expect.hasAssertions();
let Response = `{
"access_token": "thisisatoken",
"token_type": "Bearer",
"expires_in": 3599
}`;
axios.mockReturnValue(() =>
Promise.resolve({
data: Response
})
);
let response = await AuthService.getAuthToken();
expect(axios).toHaveBeenCalledTimes(1);
expect(response).toEqual("thisisatoken");
});
Beware of few things:
jest.mock is hoisted to the top even if it's declared somewhere really deep in test's code. So it's better to place all jest.mock at the top of the file - because it anyway works this way - and this way another developer would not be confused if it's mocked or not.
if using __mocks__ folder with auto mock you better inject jest.fn() in advance - most times you will like to check if part of mock has been called and with what arguments
jest.mock cannot refer to any sibling variable except those one with name starting from mock.... See Service mocked with Jest causes "The module factory of jest.mock() is not allowed to reference any out-of-scope variables" error with really good explanation.
it'd be hard(near to impossible) to unmock module partially. so consider your test either mock some module or does not mock at all to test it.

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