Mock-axios-adapter not mocking get request - reactjs

I'm trying to test this function:
export const fetchCountry = (query) => {
return dispatch => {
dispatch(fetchCountryPending());
return axios.get(`${process.env.REACT_APP_API_URL}/api/v1/countries/?search=${query}`)
.then(response => {
const country = response.data;
dispatch(fetchCountryFulfilled(country));
})
.catch(err => {
dispatch(fetchCountryRejected());
dispatch({type: "ADD_ERROR", error: err});
})
}
}
Here is my test:
describe('country async actions', () => {
let store;
let mock;
beforeEach(() => {
mock = new MockAdapter(axios)
store = mockStore({ country: [], fetching: false, fetched: false })
});
afterEach(() => {
mock.restore();
store.clearActions();
});
it('dispatches FETCH_COUNTRY_FULFILLED after axios request', () => {
const query = 'Aland'
mock.onGet(`/api/v1/countries/?search=${query}`).reply(200, country)
store.dispatch(countryActions.fetchCountry(query))
.then(() => {
const actions = store.getActions();
expect(actions[0]).toEqual(countryActions.fetchCountryPending())
expect(actions[1]).toEqual(countryActions.fetchCountryFulfilled(country))
});
});
When I run this test, I get an error UnhandledPromiseRejectionWarning and that fetchCountryPending was not received and that fetchCountryRejected was. It seems as if onGet() is not doing anything. When I comment out the line
mock.onGet('/api/v1/countries/?search=${query}').reply(200, country), I end up getting the exact same result, making me believe that nothing is being mocked. What am I doing wrong?

I couldn't get the .then(() => {}) to work, so I turned the function into an async function and awaited the dispatch:
it('dispatches FETCH_COUNTRY_FULFILLED after axios request', async () => {
const query = 'Aland'
mock.onGet(`/api/v1/countries/?search=${query}`).reply(200, country)
await store.dispatch(countryActions.fetchCountry(query))
const actions = store.getActions();
expect(actions[0]).toEqual(countryActions.fetchCountryPending())
expect(actions[1]).toEqual(countryActions.fetchCountryFulfilled(country))
});

Related

Same function for all queries onSuccess react-query

I have a use-case where I would like to run the same function onSuccess for all mutations and queries globally instead of having to set the same function on each individual query (i have a lot of queries)
I have a bunch of queries like so
const q1 = useQuery(
"q1",
async () => {
return await axios
.get(`/some/path`)
.then((res) => res.data)
.catch((e) => CustomError(e));
},
{
onSuccess: () => generic(),
}
);
const q2 = useQuery(
"q2",
async () => {
return await axios
.get(`/some/path`)
.then((res) => res.data)
.catch((e) => CustomError(e));
},
{
onSuccess: () => generic(),
}
);
const q1 = useQuery(
"q3",
async () => {
return await axios
.get(`/some/path`)
.then((res) => res.data)
.catch((e) => CustomError(e));
},
{
onSuccess: () => generic()
}
);
function generic() {
return "should be set globally and run on ever OnSuccess event"
}
However, I would like to set this globally for all quires, something like this
const queryCache = new QueryClient({
defaultConfig: {
queries: {
onSuccess: () => {
return "should be set globally and run on ever OnSuccess event";
},
},
},
});
const q1 = useQuery("q1", async () => {
return await axios
.get(`/some/path`)
.then((res) => res.data)
.catch((e) => CustomError(e));
});
const q2 = useQuery("q2", async () => {
return await axios
.get(`/some/path`)
.then((res) => res.data)
.catch((e) => CustomError(e));
});
const q1 = useQuery("q3", async () => {
return await axios
.get(`/some/path`)
.then((res) => res.data)
.catch((e) => CustomError(e));
});
I have searched the docs for about an hour for this type of functionality but cannot find anything
I was able to find out how to solve this for my use-case, it was a case of setting the OnSuccess function using setDefaultOptions.
turns out this code
const queryCache = new QueryClient({
defaultConfig: {
queries: {
onSuccess: () => {
return "should be set globally and run on ever OnSuccess event";
},
},
},
});
wasn't doing anything, instead i set the defaults through the function
const queryCache = new QueryClient();
queryCache.setDefaultOptions({
queries: {
refetchOnWindowFocus: false,
onSuccess: () => console.log("Got IM!"),
},
});
This triggers console.log("Got Im!") onSuccess for every time i call my API which is the desired outcome for my use-case.
i can see that const queryCache = new QueryClient(); does have a constructor that takes defaultOptions however, for whatever reason they do not set.
EDIT
turns out it does work passing it to the constructor, its just this code was written when using an older version of react-query when the key was defaultConfig instead of defaultOptions. This code also works (aswell as the solution above)
const queryCache = new QueryClient({
defaultOptions: {
queries: {
onSuccess: () => console.log("Got IM!"),
},
},
});
There is an open PR for that exact use case: https://github.com/tannerlinsley/react-query/pull/2404
It adds the possibility to have a global onSuccess callback on the queryCache.

Calling axios request one after the other?

I have tow functions in my ReactJs application called,
AuthService.addUser(newUser);
AuthService.userCategories(usercategories);
I want to run these two functions separately, which means the Axios request of the second function should be called after the Axios request of the first function when clicked the submit button. How do I approach the solution? Thanks in advance.
I tried in this way. Is this correct?
const handleSubmit = (e) => {
e.preventDefault();
AuthService.addUser(newUser);
AuthService.userCategories(usercategories);
};
Here are my two functions
addUser: (user) => {
//console.log(post);
axios
.post(CONSTANTS.HOSTNAME + "/api/users/register", user)
.then((res) => {
//save to local storage
const { token } = res.data;
localStorage.setItem("jwtToken", token);
isAuthenticated.next(true);
setAuthToken(token);
Swal.fire({
icon: "success",
title: "Signup Successful!",
showConfirmButton: false,
timer: 1500,
}).then(() => {
window.location.href = "/";
//decode token to get user data
const decoded = jwt_decode(token);
currentUser.next(decoded);
console.log(decoded);
});
})
.catch((err) => {
console.log(err.response.data);
Swal.fire({
icon: "error",
title: "Oops...",
text: err.response.data,
});
// alert(JSON.stringify(err.response.data));
});
},
userCategories: (userCategories) => {
axios
.post(CONSTANTS.HOSTNAME + "/api/users/usercategories", userCategories)
.then((res) => {
console.log(res.data);
});
},
just use promise if function return promise:
const handleSubmit = async (e) => {
e.preventDefault();
await AuthService.addUser();
await AuthService.userCategories();
};
or make promise from function and run async
function one() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('resolve one')
return resolve("i am after five seconds")
},
2000);
});
}
function two() {
return new Promise((resolve, reject) => {
console.log('resolve two')
return resolve("i am after three seconds")
});
}
const handleSubmit = async () => {
console.log('run handleSubmit')
await one();
await two();
}
handleSubmit()

How to mock a fetch call that is within an arrow function?

I'm trying to test the invocation of a function that deletes specific data saved in a database in React. The problem is I want to only mock the fetch call and have everything else run as usual because right now whenever tests are run the data gets deleted in the database.
Here is my code for the delete function:
deleteEvent = async () => {
try {
await fetch(
"api url",
{
method: "DELETE",
}
)
.then((res) => res.json())
.then(
(result) => {
console.log(result);
},
(error) => {
console.log(error);
}
);
} catch (error) {
console.error(error);
}
this.props.history.push("/eventList");
};
And here is my test code:
test("deleteEvent function works", (done) => {
const mockSuccessResponse = {};
const mockJsonPromise = Promise.resolve(mockSuccessResponse);
const mockFetchPromise = Promise.resolve({
json: () => mockJsonPromise,
});
jest.spyOn(global, "fetch").mockImplementation(() => mockFetchPromise);
const historyMock = { push: jest.fn() };
const wrapper = shallow(<EventState history={historyMock} />);
wrapper.instance().deleteEvent();
expect(global.fetch).toHaveBeenCalledTimes(1);
expect(historyMock.push.mock.calls[0]).toEqual(["/eventList"]);
global.fetch.mockClear();
done();
});
I get number times called: 0 for the expect(global.fetch).toHaveBeenCalledTimes(1);
and a Received: undefined for the expect(historyMock.push.mock.calls[0]).toEqual(["/eventList"]);
Any help would be great
Instead of using spyOn(global, fetch), try this:
global.fetch = jest.fn().mockImplementation(() => mockFetchPromise);
const historyMock = { push: jest.fn() };
const wrapper = shallow(<EventState history={historyMock} />);
wrapper.instance().deleteEvent();
expect(global.fetch).toHaveBeenCalledTimes(1);
expect(historyMock.push.mock.calls[0]).toEqual(["/eventList"]);
global.fetch.mockClear();
done();
});

Rematch/Effects- Error running test cases written with react testing Library

I am trying to write unit test cases for my models using react testing library but i am facing some issues executing the test cases.
My effects.js
export async function getStoredData(param1, store, param2) {
try {
dispatch(setLoading(true, 'getStoredData'));
// Check if key exists in store
const inputKeyCode = getInputKeyCode([param1, param2]);
let response = getUserDataState(store)[inputKeyCode];
if (!response) {
response = await getUserApi(param1, param2);
this.setUserData({ keyCode: inputKeyCode, keyValue: response });
}
return response;
} catch (error) {
// dispatch error
} finally {
dispatch(setLoading(false, 'getStoredData'));
}
}
My reducers.js
const INITIAL_STATE = {
userData: {},
};
const setUserData = (state, { key, value }) => ({ // {key: value}
...state,
userData: {
...state.userData,
[key]: value,
},
});
effects.test.js
import { getUserApi } from '../../../api/common';
jest.mock('../../../store', () => ({ dispatch: jest.fn() }));
jest.mock('../../../api/common', () => ({ getUserApi: jest.fn() }));
describe('getStoredData', () => {
const responseData = {};
setWith(responseData, 'data.userInformation', 12345);
const setUserData = jest.fn();
test('success', async () => {
getUserApi.mockResolvedValue(responseData);
await testModel.effects().getStoredData.call({ setuserData });
expect(setuserData).toHaveBeenCalled();
expect(setuserData).toHaveBeenCalledWith(12345);
});
test('failure', async () => {
getUserApi.mockRejectedValue(errorMsg);
await testModel.effects().getStoredData.call({ setuserData });
expect(showNotification).toHaveBeenCalled();
expect(showNotification).toHaveBeenCalledWith('error');
});
});
This gives me below error-
Expected mock function to have been called, but it was not called.
At line- expect(setuserData).toHaveBeenCalled();
Can someone help me understand what i am doing wrong? I guess i am doing some mistake in calling the setuserData. Any help is much appreciated.

Redux mock store only returning one action when multiple actions are dispatched

I'm trying to mock this axios call:
export const fetchCountry = (query) => {
return dispatch => {
dispatch(fetchCountryPending());
return axios.get(`${process.env.REACT_APP_API_URL}/api/v1/countries/?search=${query}`)
.then(response => {
const country = response.data;
dispatch(fetchCountryFulfilled(country));
})
.catch(err => {
dispatch(fetchCountryRejected());
dispatch({type: "ADD_ERROR", error: err});
})
}
}
Which on a successful call, should dispatch both action creators fetchCountryPending() and fetchCountryFullfilled(country). When I mock it like so:
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
// Async action tests
describe('country async actions', () => {
let store;
let mock;
beforeEach(function () {
mock = new MockAdapter(axios)
store = mockStore({ country: [], fetching: false, fetched: true })
});
afterEach(function () {
mock.restore();
store.clearActions();
});
it('dispatches FETCH_COUNTRY_FULFILLED after axios request', () => {
const query = 'Aland Islands'
mock.onGet(`${process.env.REACT_APP_API_URL}/api/v1/countries/?search=${query}`).replyOnce(200, country)
store.dispatch(countryActions.fetchCountry(query))
const actions = store.getActions()
console.log(actions)
expect(actions[0]).toEqual(countryActions.fetchCountryPending())
expect(actions[1]).toEqual(countryActions.fetchCountryFulfilled(country))
});
});
The second expect fails and console.log(actions) only shows an array with the one action, but it should contain both actions, fetchCountryPending and fetchCountrySuccess. When I log ('dispatched'), it shows the second action is getting dispatched in the terminal.
Can you try making your it block async and dispatch the action. I believe the tests are running before your get requests return the value
I couldn't get a then(() => {}) block to work but I was able to await the function and make it async:
it('dispatches FETCH_COUNTRY_FULFILLED after axios request', async () => {
const query = 'Aland Islands'
mock.onGet(`${process.env.REACT_APP_API_URL}/api/v1/countries/?search=${query}`).replyOnce(200, country)
await store.dispatch(countryActions.fetchCountry(query))
const actions = store.getActions()
console.log(actions)
expect(actions[0]).toEqual(countryActions.fetchCountryPending())
expect(actions[1]).toEqual(countryActions.fetchCountryFulfilled(country))
});
});

Resources