I am trying to mock the axios's request method. But it throwing error
it('should execute axios request method once', async () => {
jest.mock('axios');
axios.request.mockImplementation(() =>
Promise.resolve({
data: {}
})
);
const requestObj = {
method: 'GET',
url: 'http://mock.url',
headers: {}
};
await request(requestObj);
expect(axios.request).toHaveBeenCalledTimes(1);
});
request.js
export default async (request, httpService = axios) => {
const { method, data, headers } = request;
let { url } = request;
const token = getLocalstorage('token');
if (token) {
headers.token = token;
}
if (method === 'GET') {
if (data) {
url += `?${serialize(data)}`;
}
}
return httpService
.request({
method,
url,
headers: Object.assign({}, headers),
...(method !== 'GET' && { data })
})
.then(successResponse, error => {
throwHttpError(error);
});
};
error
Here is the solution based on:
"jest": "^24.8.0",
"ts-jest": "^24.0.2",
"typescript": "^3.5.3"
"axios": "^0.19.0",
request.ts:
import axios from 'axios';
const serialize = data => data;
const getLocalstorage = key => key;
const successResponse = () => console.log('successResponse');
const throwHttpError = error => new Error(error);
export default async (request, httpService = axios) => {
const { method, data, headers } = request;
let { url } = request;
const token = getLocalstorage('token');
if (token) {
headers.token = token;
}
if (method === 'GET') {
if (data) {
url += `?${serialize(data)}`;
}
}
return httpService
.request({
method,
url,
headers: Object.assign({}, headers),
...(method !== 'GET' && { data })
})
.then(successResponse, error => {
throwHttpError(error);
});
};
Unit test, request.spec.ts
import request from './request';
import axios from 'axios';
jest.mock('axios');
describe('request', () => {
it('should execute axios request method once', async () => {
(axios.request as jest.Mock<any, any>).mockResolvedValueOnce({ data: 'mocked data' });
const requestObj = {
method: 'GET',
url: 'http://mock.url',
headers: {}
};
await request(requestObj);
expect(axios.request).toHaveBeenCalledTimes(1);
});
});
Unit test result:
PASS src/stackoverflow/57353897/request.spec.ts
request
✓ should execute axios request method once (13ms)
console.log src/stackoverflow/57353897/request.ts:4
successResponse
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.637s, estimated 3s
Related
Can someone tell me what mistake I am making or tell me how to set the header in axios patch request. when I am running the API through postman, everything is working fine but when I connect it with the front end, an error comes up saying that the JWT is not provided on the backend
here is the frond end code :
import React, { useEffect } from 'react';
import { useParams } from 'react-router';
import axios from 'axios';
const Loader = () => {
const parmas = useParams();
const { id } = parmas;
console.log(id);
useEffect(() => {
const fetchBags = async () => {
try {
const res = await axios.patch('http://localhost:4001/public/verify', {
headers: {
'Content-Type': 'application/json',
Token: id,
},
});
console.log(res);
console.log('CBM', { res });
} catch (error) {
console.log(error);
}
};
fetchBags();
}, []);
return <div>this is loader</div>;
};
export default Loader;
below is my backend code:
export const verifyUser = async (data) => {
const token1 = data.header("Token");
try {
const verified = jwt.verify(token1, getTokenSecret());
console.log(verified)
await userModel.verifyUser(verified);
return {
message: "success",
};
} catch (error) {
console.log(`Auth Service > verifyUser > ${error.toString()}`);
throw error;
}
};
this error is comming:
Error
From docs
axios.patch(url[, data[, config]])
As you can see you pass config in 3rd argument not 2nd.
const res = await axios.patch(
'http://localhost:4001/public/verify',
{}, // data (2nd argument)
{
headers: {
'Content-Type': 'application/json',
Token: id,
},
} // config (3rd argument)
)
I'm using a middleware to verify token with this code:
import { Request, Response, NextFunction } from "express";
import jwt from "jsonwebtoken";
class VerifyToken {
public verify(req: Request, res: Response, next: NextFunction) {
try {
const authHeader = req.headers["authorization"];
const token = authHeader?.split(" ")[1];
const signature = process.env.JWT_SIGNATURE;
jwt.verify(token, signature);
next();
} catch (error) {
return res.status(401).json("Acess denied");
}
}
}
export default new VerifyToken().verify;
I'm using Jest to test this middleware, here's the code:
import dotenv from "dotenv";
import { NextFunction, Request, Response } from "express";
import verifyToken from "../../src/middlewares/verifyToken";
describe("Verify token", () => {
let mockRequest: Partial<Request>;
let mockResponse: Partial<Response>;
let nextFunction: NextFunction = jest.fn();
beforeAll(() => {
dotenv.config({ path: ".env" });
});
beforeEach(() => {
mockRequest = {};
mockResponse = {
json: jest.fn(),
};
});
it("should verify token with a invalid token", () => {
const token = process.env.TEST_FALSE_TOKEN;
mockRequest = {
headers: {
authorization: `bearer ${token}`,
},
};
verifyToken(mockRequest as Request, mockResponse as Response, nextFunction);
expect(mockResponse.status).toBe(401);
});
it("should verify token with a valid token", () => {
const token = process.env.TEST_TOKEN;
mockRequest = {
headers: {
authorization: `bearer ${token}`,
},
};
verifyToken(mockRequest as Request, mockResponse as Response, nextFunction);
expect(nextFunction).toBeCalledTimes(1);
});
});
When I run a test using Jest, it shows the following error:
TypeError: res.status is not a function
I've already tried to use ErrorRequestHandler with the request but I get the same error.
How can I fix this? Thanks for the help.
Your mocked response doesn't have a status function on it..
mockResponse = {
json: jest.fn(),
status: jest.fn().mockReturnThis(),
};
I have tried so many thing but my react app is not recieving jsonData variable or res as a return from the node app. The app is working and printing to console on the node side but I can't get it to print onto the react side.
const submitForm = async (event) => {
event.preventDefault(); // Prevent default submission
const data2 = document.getElementById("miles").value;
const data =
"passenger_vehicle-vehicle_type_" +
carType +
"-fuel_source_" +
vehicleType +
"-engine_size_na-vehicle_age_na-vehicle_weight_na";
axios
.post(`http://localhost:8000/api/vehicle/`, { data, data2 })
.then((res) => {
const returnText = res.json();
console.log(returnText);
return res.json();
})
.then((jsonData) => {
console.log(jsonData);
return;
})
.catch((error) => {
console.log("got errr while posting data", error);
});
};
I edited out the api and api key.
var fetch = require('node-fetch');
exports.vehicle = (req, res) =>{
let status;
const { data, data2 } = res.body;
const values = {
"emission_factor": data,
"parameters": {
"distance": parseInt(data2),
"distance_unit": "mi",
},
};
fetch('https://AAAAAAAAAAAAAAAA', {
method: 'POST',
headers: {
'Authorization': 'Bearer MYAPIKEY',
'Content-Type': 'application/json'
},
body: JSON.stringify(values)
})
.then((res) => {
status = res.status;
return res.json()
})
.then((jsonData) => {
console.log(jsonData);
console.log(status);
return jsonData
})
.catch((err) => {
// handle error
console.error(err);
});
res.send(req.body);
}
Working code thanks for the help:
const submitForm = async (event) => {
event.preventDefault(); // Prevent default submission
const data2 = document.getElementById("miles").value;
const data =
"passenger_vehicle-vehicle_type_" +
carType +
"-fuel_source_" +
vehicleType +
"-engine_size_na-vehicle_age_na-vehicle_weight_na";
axios
.post(`http://localhost:8000/api/vehicle/`, { data, data2 })
.then((res) => {
console.log(res.data);
return;
})
.catch((error) => {
console.log("got err while posting data", error);
});
};
Node solution in comments.
The functions inside your then() statements need to return data e.g. then((res) => {return res.json()})
You have two problems here...
Client-side, you seem to be mixing up an Axios response with a fetch() Response. You want res.data, not res.json(). Since you've tagged this with reactjs, here is where you would set the data to a state value, eg
axios.post(...).then(res => {
setSomeState(res.data)
})
Server-side, you aren't waiting for your fetch request to complete. I'd recommend using an async function
exports.vehicle = async (req, res) => {
try {
const { data, data2 } = req.body
const values = {
"emission_factor": data,
"parameters": {
"distance": parseInt(data2),
"distance_unit": "mi",
},
}
// don't mix up the Express "res" with the fetch "response"
const response = await fetch('https://AAAAAAAAAAAAAAAA', {
method: 'POST',
headers: {
'Authorization': 'Bearer MYAPIKEY',
'Content-Type': 'application/json'
},
body: JSON.stringify(values)
})
if (!response.ok) {
throw new Error(`${response.status}: ${await response.text()}`)
}
res.json(await response.json()) // respond with the data
} catch (err) {
console.error(err)
res.status(500).send(err)
}
}
I'm calling two methods in useeffect but when api1 get called it get executed till await axios.put and then api2 get called without getting the response from api1. And after getting a reponse from api2 it goes to api1 reponse and gets a reponse as undefined
useEffect(() => {
api1();
api2();
}, [])
const api1 = async () => {
try {
var requestModel = JSON.stringify({ UserId: userid, MenuName: menuname });
var requestBody = security.encrypt(requestModel);
axios.myHashData = security.computedHmac256(requestBody);
var config = { headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } }
await axios.put(axios.controllername + 'methodname', requestBody, config, { withCredentials: true })
.then(response => {
if (response.status === 200) {
//setapidata(response.data);
}
});
} catch (error) {
console.error(error.message)
}
}
const api2= async () => {
try {
var requestModel = JSON.stringify({ UserID: userid });
var requestBody = security.encrypt(requestModel);
axios.myHashData = security.computedHmac256(requestBody);
var config = { headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } }
await axios.post(axios.controllername + 'methodname', requestBody, config, { withCredentials: true })
.then(response => {
if (response.status === 200) {
setapidata(response.data);
GetApis();
}
});
} catch (error) {
console.error(error.message)
}
}
If you want to wait for the api1 to execute before api2 is called you need to change the code for useEffect as below
useEffect(async() => {
await api1();
await api2();
}, [])
or
useEffect(() => {
api1().then(() => api2());
}, [])
To handle errors properly use try-catch block inside the useEffect if using await or use catch block if using .then
Also, another suggestion, if you are using a function inside useEffect make sure either the function is defined inside the same useEffect block or it is memorized either via useCallback or useMemo
I want to test the function below using the tests below. However, I fail in mocking the userService.login function, which always returns undefined instead of desired value. I have tried these approaches https://jestjs.io/docs/mock-functions#mock-return-values (please see the tests), but nothing seems to work.
FUNCTION
static async login(req: Request, res: Response, next: NextFunction): Promise<void> {
const userDTO = req.body;
try {
const accessToken = await userService.login(userDTO);
// accessToken is undefined here :(
res.setHeader('authorisation', `Bearer ${accessToken}`);
res.send('Logged in');
} catch (e) {
// eslint-disable-next-line callback-return
next(e);
}
}
TESTS:
import UserService from '#services/users.service';
import UserController from '#routers/controller/users.controller';
import { NextFunction, Request, Response } from 'express';
import UserRepository from '#data-access/user.repository';
jest.mock('#services/users.service');
let userService: UserService;
const fakeToken = 's$sdfDgf45d';
beforeAll(() => {
userService = new UserService(UserRepository);
// userService.login = jest.fn().mockResolvedValue('s$sdfDgf45d').mockRejectedValue('error');
});
describe('UserController: ', () => {
const mockReq = {
body: {
login: 'Artyom',
password: 'qwerty123'
}
};
const mockRes = {
setHeader: jest.fn(),
send: jest.fn()
};
const nextFunction = jest.fn();
test('the class constructor should have been called', () => {
expect(UserService).toHaveBeenCalled();
});
test('should send auth token in case of success scenario', async () => {
// #ts-ignore
userService.login.mockResolvedValue('s$sdfDgf45d').mockRejectedValue('error');
// jest.spyOn(userService, 'login').mockResolvedValue('s$sdfDgf45d' as any).mockRejectedValue('error');
await UserController.login(mockReq as Request, mockRes as any as Response, nextFunction as NextFunction);
// that's the test that fails
expect(mockRes.setHeader.mock.calls[0]).toEqual(['authorisation', `Bearer ${fakeToken}`]);
expect(mockRes.send.mock.calls[0][0]).toEqual('Logged in');
});
test('should throw an error', async () => {
return expect((userService.login as jest.Mock)()).rejects.toMatch('error');
});
});
ERROR:
Array [
"authorisation",
- "Bearer s$sdfDgf45d",
+ "Bearer undefined",
CODE:
https://github.com/lion9/node-express