Jest Mock changes to undefined from one line to another - javascript

I want to test my AWS Lambda functions written in typescript. The problem is that I'm mocking a service, which initially works fine and has all expected functions, but from one line to the other it turns to undefined, even though in the console.log right before it is defined.
My lambda function:
export const changePassword = LambdaUtils.wrapApiHandler(
async (event: LambdaUtils.APIGatewayProxyEvent, context: Context) => {
const accessToken = event?.body?.accessToken;
const previousPassword = event?.body?.previousPassword;
const proposedPassword = event?.body?.proposedPassword;
// let authenticationService = Injector.getByName("AuthenticationService"); // I tried mocking this object but given the dependency injection it is always undefined
//Request
const authenticationService = new AuthenticationService(); // Here it is perfectly mocked
console.log("-> authenticationService", authenticationService); // Here it is also defined with all functions
const response = await authenticationService.changePassword(
accessToken,
previousPassword,
proposedPassword
); //Here authenticationService is undefined
console.log("response in auth_handler", response);
if (response?.statusCode === 200) {
return new LambdaUtilities().createHttp200Response(response);
}
}
);
My test component:
jest.mock("../../../controllers/auth.service");
const MockedAuthService = AuthenticationService as jest.Mock<AuthenticationService>;
.....
test(`Test successful response of changePassword Repo function"`, async () => {
const accessToken = "accessToken";
const previousPassword = "123456";
const proposedPassword = "654321";
const mockAuthService = new MockedAuthService() as jest.Mocked<AuthenticationService>;
console.log("-> mockAuthService", mockAuthService);
const event = {
body: {
accessToken,
proposedPassword,
previousPassword
}
};
const response = await changePassword(event, {}, {});
console.log("response", response);
expect(response).not.toBeNull();
});
Here are screenshots from a debug with PyCharm:

Related

async axios get call axios returns nothing

I'm trying to get the current temperature from openweathermaps using axios.
I can put my url into the browser and it works fine. But when I try do an axios call with the correct url it doesnt event call.
Here is the relevant code section:
function Overview() {
const [temperature, setTemperature] = useState('')
const API_KEY = '{apikey}'
const getTemperature = useCallback(async () => {
const url = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude.toFixed(2)}&lon=${longitude.toFixed(
2,
)}&appid=${API_KEY}`
console.log('my provided url is set to:', url)
const response = await axios.get(url)
if (response.data) {
setTemperature(response.data.main.temp)
}
}, [latitude, longitude])
useEffect(() => {
getTemperature().catch(console.error)
}, [getTemperature])
return <>{temperature ? temperature : 'no data'}</>
}
Any help as to where I'm going wrong would be great as I just cant see my error!
Your URL working on the browser showcases that your API key is correct. Now, make sure the variables in your URL are properly set from the code, particularly the API key. you can console.log them to be sure. Passed that, the minimal code below would do.
import {useCallback, useEffect} from 'react';
import axios from 'axios';
function Overview() {
// YOUR_INITIAL_STATE rather be undefined
const [temperature, setTemperature] = useState(YOUR_INITIAL_STATE);
const API_KEY = 'YOUR_API_KEY';
const getTemperature = useCallback(async () => {
const url = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}`;
console.log("my provided url is set to:", url);
const response = await axios.get(url);
if(response.data){
setTemperature(response.data.main.temp);
}
// only call the functtion when these deps change
}, [latitude, longitude])
useEffect(() => {
// Check the error for further debbugging
getTemperature()
.catch(console.error);
}, [getTemperature])
return (
<>
{temperature ? temperature : "no data"}
</>
);
}
It looks like your arrow function is calling itself recursively:
const setTemperature = async () => {
const response = await axios.get(`https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}`);
setTemperature(response.data.main.temp);
};
Your code doesn't show where this is being called. Probably it needs to be something like:
const setTemperature = async () => {
const response = await axios.get(`https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}`);
return response.data.main.temp;
};
Perhaps it just needs to be a one letter change:
const getTemperature = async () => {
const response = await axios.get(`https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}`);
setTemperature(response.data.main.temp);
};
Credit to #devklick

Undefined end-point http://localhost:8010/undefined/

I am trying to send data from front-end to back-end. Here is my front:
import axios from 'axios'
const api = axios.create({
baseURL: `${process.env.BASE_FRONT_URL}`, // process.env.BASE_FRONT_URL = http://localhost:8010
})
export const postTip = async (payload) => {
try {
const { data } = await api.post(`post-tip`, payload);
return data;
} catch (e) {
return [];
}
};
And here is back-end:
const router = require('express').Router();
const tipController = require('../controllers/tips/tipController')
router.post('post-tip', tipController.postTip);
That function tipController.postTip actually just receive and shows data, but when I trigger this end-point I get error: POST http://localhost:8010/undefined/post-tip 404 (Not Found). So, what's wrong with end-point and how can I make it work? Also, I have no idea, where does this undefined come from? Am I missing something?
I have found my mistake. Actually process.env.BASE_FRONT_URL was really undefined, so, I made it like that:
import axios from 'axios'
const api = axios.create({
baseURL: 'http://localhost:8084',
})
export const postTip = async (payload) => {
try {
const { data } = await api.post(`post-tip`, payload);
return data;
} catch (e) {
return [];
}
};
But the most important thing is that on back-end I have to use the same port (8084)

mockup axios with create and post, jestjs

I am currently realized I shouldn't be calling api straight through network request while using jestjs to check for api.
I have been looking at some posts + youtube tutorials such as https://www.leighhalliday.com/mocking-axios-in-jest-testing-async-functions Mock inner axios.create() but still a bit confused and now sure how to get this to work.
I created a registration api and wanted to do test on it, and after reading the mockup documentation and so on. I have something like...this as my folder structure
this is how my base_axios/index.js looks like, BASE_URL is just something like http://localhost:3000
const axios = require('axios');
const { BASE_URL } = require('../base');
const baseOption = {
// without adding this, will not be able to get axios response status
validateStatus: function (status) {
return status >= 200 && status <= 503;
},
baseURL: BASE_URL,
headers: { 'Content-Type': 'application/json' },
};
module.exports = axios.create(baseOption);
apis/auth.js
const request = require('./base_axios');
module.exports = {
register: data => request.post('/auth/register', data),
};
mocks/axios.js
const mockAxios = jest.genMockFromModule('axios');
mockAxios.create = jest.fn(() => mockAxios);
module.exports = mockAxios;
routes/auth/register.js
const Auth = require('../../apis/auth');
const mockAxios = require('axios');
test('calls axios for registration', async () => {
// this should give me an error and show which api has been called
expect(mockAxios.post).toHaveBeenCalledWith('what is the api');
const response = await Auth.register();
console.log(response, 'response'); // response here gives me undefined
});
I am not getting which api call is being called and te response gives me undefined
also getting this error from jest expect(jest.fn()).toHaveBeenCalledWith(...expected)
Thanks in advance for anyone with advice and suggestions.
PS
jest.config.js
module.exports = {
clearMocks: true,
coverageDirectory: "coverage",
// The test environment that will be used for testing
testEnvironment: "node",
};
You can mock axios and an implemtation for it as below:
jest.spyOn(axios, 'post').mockImplementation();
For an example:
test('calls axios for registration', async () => {
const mockDataRequest = {};
const mockPostSpy = jest
.spyOn(axios, 'post')
.mockImplementation(() => {
return new Promise((resolve) => {
return resolve({
data: {},
});
});
});
expect(mockPostSpy).toHaveBeenCalledTimes(1);
expect(mockPostSpy).toBeCalledWith(
`/auth/register`,
expect.objectContaining(mockDataRequest)
);
});

How to handle Promise in custom react useEffect

I have two components which i am working with. In the first component, i made a custom useEffect hook that retrieves data from my server. Please see the code below:
Code snippet One
import {useState, useCallback} from 'react';
import {stageQuizApi} from '../api/quiz';
import {QuestionService} from "../services/IdDbServices/question_service";
const usePostData = ({url, payload, config}) => {
const [res, setRes] = useState({data: null, error: null, isLoading: false});
const callAPI = useCallback(() => {
setRes(prevState => ({...prevState, isLoading: true}));
stageQuizApi.patch(url, payload, config).then( res => {
setRes({data: res.data, isLoading: false, error: null});
const questionInDb = {};
const {NoTimePerQuestion,anwser, question, playerDetails, option} = res.data.data;
const {playerid,anwserRatio, name} = playerDetails
questionInDb.timePerQuestion = NoTimePerQuestion;
questionInDb.anwserRatio = anwserRatio;
questionInDb.options = option;
questionInDb.answer = anwser;
questionInDb.playerId = playerid;
questionInDb.name = name;
questionInDb.question = question;
const Service = new QuestionService();
Service.addStudent(questionInDb).
then(response=>console.log(response))
.catch(err=>console.log(err));
}).catch((error) => {
console.log(error)
if (error.response) {
const errorJson = error.response.data
setRes({data: null, isLoading: false, error: errorJson.message});
} else if (error.request) {
setRes({data: null, isLoading: false, eror: error.request});
} else {
setRes({data: null, isLoading: false, error: error.message});
}
})
}, [url, config, payload])
return [res, callAPI];
}
export default usePostData;
The above module has two purpose. It first makes an axios request to my endpoint and secondly makes a database insertion to browser IndexDb (similar to localstorage but with sql approach) ( like inserting data into the database using the response that was gotten from the first request. so typically i have a promise in the outer .then block. This part:
Code snippet Two
const questionInDb = {};
const {NoTimePerQuestion,anwser, question, playerDetails, option} = res.data.data;
const {playerid,anwserRatio, name} = playerDetails
questionInDb.timePerQuestion = NoTimePerQuestion;
questionInDb.anwserRatio = anwserRatio;
questionInDb.options = option;
questionInDb.answer = anwser;
questionInDb.playerId = playerid;
questionInDb.name = name;
questionInDb.question = question;
const Service = new QuestionService();
Service.addStudent(questionInDb).
then(response=>console.log(response))
.catch(err=>console.log(err));
Here is the problem, I am trying to maintain state as i want the result of this module to be shared in another route and i don't want to hit the server again hence i inserted the result into indexDb browser storage. Here is the code that executes the above module:
Code snippet Three
const displaySingleQuestion = ()=>{
OnUserGetQuestion();
history.push('/player/question');
}
The above method is called from my first route /question and it is expected to redirect user to the /player/question when the displaySingleQuestion is called.
On the new route /player/question i then want to fetch the data from IndexDb and update the state of that component using the useEffect code below:
Code snippet Four
useEffect(()=>{
const getAllUserFromIndexDb = async()=>{
try{
const result = await new Promise((resolve, reject) => {
service.getStudents().then(res=>resolve(res)).catch(err=>reject(err))
});
console.log('it did not get to the point i was expecting',result)
if(result[0]){
console.log('it got to the point i was expecting')
const singleQuestion = result[0];
const questionPage = playerQuestionToDisplay;
questionPage.name = singleQuestion.name;
questionPage.anwserRatio = singleQuestion.anwserRatio;
questionPage.answer = singleQuestion.answer;
questionPage.options = singleQuestion.options;
questionPage.playerId = singleQuestion.playerId;
questionPage.question = singleQuestion.question;
questionPage.timePerQuestion = singleQuestion.timePerQuestion;
return setplayerQuestionToDisplay({playerQuestionToDisplay:questionPage})
}
}
catch(error){
console.log(error)
}
}
getAllUserFromIndexDb();
return function cleanup() {
setplayerQuestionToDisplay({playerQuestionToDisplay:{}})
}
},[history.location.pathname]);
The problem is that only one Button click (Code snippet three)(displaySingleQuestion()) triggers the whole functionality and redirect to the /player/question page but in this new route the state is not been set until a page reload as occurred, i tried debugging the problem and i found out that when the button is clicked i found out that Code snippet two is executed last hence when Code snippet Four ran it was in promise and until a page reloads occurs the state of the component is undefined
Thanks for reading, Please i would appreciate any help in resolving this issue.

How do I mock constructor state initialisation with Jest

New to node.js. I am writing a JS API client that wraps the underlying axios library. In the unit tests I am mocking axios using Jest.
In the constructor of my API class I pass in a URL, and use the axios.create function to create a custom instance of axios and bind it to the the client property.
The problem arises when I mock the axios dependency with jest.mock('axios') - A TypeError is being thrown in the test when an attempt is made to call axios.get:
TypeError: Cannot read property `get` of undefined
I understand why this is happening, but I've not found a way to enable me to mock axios and not have the client field be undefined. Is there a way to get around this, other than injecting axios through the constructor?
Client code and test below:
client.js
jest.mock("axios");
const axios = require("axios");
const mockdata = require("./mockdata");
const ApiClient = require("../../../src/clients/apiclient");
const BASE_URL = "https://www.mock.url.com"
const mockAxiosGetWith = mockResponse => {
axios.get.mockResolvedValue(mockResponse);
};
test("should make one get request", async () => {
mockAxiosGetWith(MOCK_RESPONSE)
// the client field in apiclient is undefined
// due to the jest module mocking of axios
const apiclient = new ApiClient.AsyncClient(BASE_URL);
// TypeError: Cannot read property `get` of undefined
return await apiclient.get("something").then(response => {
expect(axios.get).toHaveBeenCalledTimes(1);
});
});
client.test.js
const axios = require("axios");
const getClient = (baseUrl = null) => {
const options = {
baseURL: baseUrl
};
const client = axios.create(options);
return client;
};
module.exports = {
AsyncClient: class ApiClient {
constructor(baseUrl = null) {
this.client = getClient(baseUrl);
}
get(url, conf = {}) {
return this.client
.get(url, conf)
.then(response => Promise.resolve(response))
.catch(error => Promise.reject(error));
}
}
};
You need to mock axios so it will return an object which holds the create function which should return the object with the get
import axios from 'axios'
jest.mock('axios', () => ({create: jest.fn()}))
test("should make one get request", async () => {
const get = jest.fn(()=>Promise.resolve(MOCK_RESPONSE))
axios.create.mockImplementation(()=>({get}))
const apiclient = new ApiClient.AsyncClient(BASE_URL);
await apiclient.get("something")
expect(get).toHaveBeenCalledTimes(1);
});

Categories

Resources