Mocking an asyc function in react using Jest - reactjs

I am new to react and jest.
I am getting stuck on the right way to mock an async function even after scouring many articles on this.
here is my scenario. I am pasting the code which is giving me trouble. I have the following function defined. I want to mock the getToken() function. The returned token is a string.
export async getSignin() {
const token = await getToken()
//do something with this token
}
export async function getToken(){
const token = (await accessToken())
return token
}
Test code:
it(" returns a valid user ", async () => {
const getToken = jest
.fn()
.mockImplementation(async () => Promise.resolve("abcd"))
const signedin = await getSignin()
}
when I do this, my expectation is that the code will use the mock implementation of the getToken and proceed. What I am getting is that it is throwing an error at accessToken(). My understanding of mock is that it should not go into the actual implementation and call accessToken()
what am I doing wrong here?

fetch-mock is a great package for mocking API requests in your test files! http://www.wheresrhys.co.uk/fetch-mock
After installing, in your beforeEach block in a test file you can now mock the payload from API calls. It will look something like this:
beforeEach(() => {
fetchMock.mock('/api/users'/1, { id: 1, name: 'Test User Name', address: 'Test User Address'})
})

Related

Using Jest Mock API Calls to SetState

I have an API call which runs whenever a certain component mounts. If this API call is successful the response data is used to update the state of one of my React Hooks.
The issue I am having is either related to asynchronicity or a poorly formatted mock API call, but no matter what I try I cannot get this test to work.
Here is a simplified version of the API:
const getOrg =() => {
axios.get(URL, config)
.then(response => response.data)
.then(data => {
setOrgTitle(data.name)
}
}
Basically the API is triggered and my setOrgTitle hook is updated from the response.
const [orgTitle, setOrgTitle] = useState("");
Now in my return statement I am displaying the value of orgTitle:
<h1 className={styles.titleText} id="document-folders-h1">
{orgTitle} Folders
</h1>
Alright, so the component is pretty simple. When I am trying to test things my two ideas were to either set the initial orgTitle hook state in my test or to mock the API. After some research I decided mocking the API was the way to go.
So I have a mockAxios component:
const mockAxios = {
get: jest.fn(() => Promise.resolve({ data: {} }))
};
module.exports = mockAxios;
And my test file:
import mockAxios from "../../mockTests/DocumentFoldersMock";
it("fetches results for getAdminOrg", () => {
axios.get.mockImplementation(() =>
Promise.resolve({ data: { name: "GETtest" } })
);
const wrapper = mount(
<AppProviders>
<DocumentFolders />
</AppProviders>
);
const orgTitle = wrapper.find("#document-folders-h1");
expect(orgTitle.text()).toContain("GETtest Folders");
});
I am mocking the response data, however I am not sure how to run the setOrgTitle function which is called in the .then of my actual axios call. How can I do this from my mock axios call using my mock response?
The result of the Jest test says expected("GETtest Folders") received(" Folders") so I am pretty sure that I am either having an issue with asynchronicity or an issue calling the hook update.

How to write JEST test cases for the callback API

I have created a API service factory, passing dynamically URL, function as a parameter. Once it success data comes in the callback function and its working fine as per expected. For same I am going to write JEST test cases. I couldn't fine the right approach to do it. Can help someone. Great appreciate.
Code is here
function userLogin(username, password) {
const reqBody = {
companyEmailAddress: username,
password,
};
const url = `${config.apiBaseUrl}${serviceMethodConstants.login}`;
return (dispatch) => {
dispatch(serviceFactory.postData(url, false, reqBody, function (response, dispatch) {
if (response !== undefined) {
console.log(response )
}
}));
};
}
For same I wrote JEST test case, but it is not showing any error or success message as expected.
JEST test code
import { userConstants } from './constants';
import { serviceFactory } from '../../services/_helpers/serviceFactory';
const loginData = {
companyEmailAddress: 'rameshffdfdf.lambanihghgh#gmail.com',
password: 'Ramesh#1',
};
axiosMock.onPost(routeUrl).reply(200, JSON.stringify(loginData));
const spy = jest.spyOn(axios, 'post');
await store.dispatch(userActions.userLogin(...loginData, function (response, dispatch) {
expect(response.message).toEqual('Failure');
expect(spy).toBeCalled();
}));
userLogin action creator (a thunk) doesn't accept a callback and doesn't do a request. It's unknown whether store.dispatch returns a promise that could be awaited.
A proper strategy for unit testing is to mock everything but tested unit. Since serviceFactory abstraction is in use, Axios shouldn't be involved. Action creator can be tested without Redux involved, either.
const dispatch = jest.fn();
const postDataResult = {};
jest.spyOn(serviceFactory, 'postData').mockReturnValue(postDataResult);
userActions.userLogin('user', 'pass')(dispatch);
expect(serviceFactory.postData).toBeCalledWith(url, false, {...}, expect.any(Function));
expect(dispatch).toBeCalledWith(postDataResult);
The test can stay synchronous this way.

integration tests - redux/react + nock.js

I have no clue how to find a way to write this integration test.
I am using enzyme for mocking react components, jest for testing and nock for mocking axios api calls.
So far I created test which simulate clicking on button and I would like to mock the api call.
In the internet there is no much help.
My test:
it('Should invoke clear action and clear the group', (done) => {
// GIVEN
const clearButtonComponent = wrapper.find('[id="123"]');
nock('http://localhost:8080')
.intercept('/path/api/brum/123/group', 'DELETE')
.reply(200, {
status: 200,
message: 'cleared',
});
const service = new myService();
// WHEN
clearButtonComponent.first().simulate('click');
const result = Promise.resolve(service.clearGroup(123));
// THEN
expect(result).toEqual({ x: 'x' }); // I know it's not what I expect
wrapper.update();
done();
});
async action redux:
export const clearGroup = id=> (dispatch, getState) => {
myService.clearGroup(id)
.then(() => {
return dispatch(getGroup(id))
});
};
method in myService:
clearGroup(id) {
return this._delete(`/${id}/group`);
}
of course path is more complex but my service extends base service which has this base url.
Can anybody tell me how to mock it to let code goes further?
It still complain that id is undefined - look like nock does not mock it.
I would drop nock (I try to only use it for testing clients these days) and mock myService with jest.
I don't use axios, so haven't used this, but it might do the trick.. https://github.com/knee-cola/jest-mock-axios.
Otherwise you could look at writing your own mock.. https://jestjs.io/docs/en/es6-class-mocks

Mock the internals of a thunk

This is a test I'm running:
it('dispatches the logout action', () => {
const store = mockStore({});
store.dispatch(logout()); // TODO: logout() has a function in its payload that gives an error
const expectedActions = store.getActions();
expect(expectedActions).toMatchSnapshot();
});
It's giving me the following error: (using this library: https://github.com/wix/react-native-navigation)
Navigation.getRegisteredScreen: login used but not yet registered
The problem is that the logout() action dispatches a thunk (async) that calls Navigation.startSingleScreenApp. I somehow need to mock this Navigation class OR mock the entire logout() action.
I've tried several things:
loginService.logout = jest.fn();
jest.spyOn(Navigation, 'startSingleScreenApp');
But none of these seem to work.
Can anyone help me? I'm familiar with mocking but I'm clueless here.
It would be helpful to look at the definition of logout(). You could fake the implementation of all the methods called within logout(). I'm not sure what the return value of startSingleScreenApp is, but you could do:
jest.spyOn(Navigation, 'startSingleScreenApp').mockImplementation(() => {
/* do something fake */
});

Return value of a mocked function does not have `then` property

I have the following async call in one of my React components:
onSubmit = (data) => {
this.props.startAddPost(data)
.then(() => {
this.props.history.push('/');
});
};
The goal here is to redirect the user to the index page only once the post has been persisted in Redux (startAddPost is an async action generator that sends the data to an external API using axios and dispatches another action that will save the new post in Redux store; the whole thing is returned, so that I can chain a then call to it in the component itself). It works in the app just fine, but I'm having trouble testing it.
import React from 'react';
import { shallow } from 'enzyme';
import { AddPost } from '../../components/AddPost';
import posts from '../fixtures/posts';
let startAddPost, history, wrapper;
beforeEach(() => {
startAddPost = jest.fn();
history = { push: jest.fn() };
wrapper = shallow(<AddPost startAddPost={startAddPost} history={history} />);
});
test('handles the onSubmit call correctly', () => {
wrapper.find('PostForm').prop('onSubmit')(posts[0]);
expect(startAddPost).toHaveBeenLastCalledWith(posts[0]);
expect(history.push).toHaveBeenLastCalledWith('/');
});
So I obviously need this test to pass, but it fails with the following output:
● handles the onSubmit call correctly
TypeError: Cannot read property 'then' of undefined
at AddPost._this.onSubmit (src/components/AddPost.js:9:37)
at Object.<anonymous> (src/tests/components/AddPost.test.js:25:46)
at process._tickCallback (internal/process/next_tick.js:109:7)
So how can I fix this? I suspect this is a problem with the test itself because everything works well in the actual app. Thank you!
Your code is not testable in the first place. You pass in a callback to the action and execute it after saving the data to the database like so,
export function createPost(values, callback) {
const request = axios.post('http://localhost:8080/api/posts', values)
.then(() => callback());
return {
type: CREATE_POST,
payload: request
};
}
The callback should be responsible for the above redirection in this case. The client code which uses the action should be like this.
onSubmit(values) {
this.props.createPost(values, () => {
this.props.history.push('/');
});
}
This makes your action much more flexible and reusable too.
Then when you test it, you can pass a stub to the action, and verify whether it is called once. Writing a quality, testable code is an art though.
The problem with your code is that the startAddPost function is a mock function which does not return a Promise, but your actual this.props.startAddPost function does return a Promise.
That's why your code works but fails when you try to test it, leading to the cannot read property.... error.
To fix this make your mocked function return a Promise like so -
beforeEach(() => {
startAddPost = jest.fn().mockReturnValueOnce(Promise.resolve())
...
});
Read more about mockReturnValueOnce here.

Resources