Jest how to mock api call - reactjs

I am trying to mock my api call with jest but for some reason it's not working. I don't really understand why. Anyone has an idea?
(the test keep call the original api call function and not the mock)
my test.js
import { getStuff } from '../stuff';
import * as api from '../../util/api';
describe('Action getStuff', () => {
it('Should call the API to get stuff.', () => {
api.call = jest.fn();
getStuff('slug')(() => {}, () => {});
expect(api.call).toBeCalled();
jest.unmock('../../util/api.js');
});
});
stuff.js redux action
import api from '#util/api';
import { STUFF, API } from '../constant';
export const getStuff = slug => (dispatch, getState) => {
const state = getState();
api.call(API.STUFF.GET, (err, body) => {
if (err) {
console.error(err.message);
} else {
dispatch({
type: STUFF.GET,
results: body,
});
}
}, {
params: { slug },
state
});
};

The import are immutable so it won't work, what you should is mock the whole module. Either with a __mock__ directory or simply with:
jest.mock('../../util/api');
const { call } = require('../../util/api');
call.mockImplementation( () => console.log("some api call"));

Related

Testing iron-session `withIronSessionSsr` that uses `context` in NextJs, React, and Jest

So I have this NextJs utility file function below that returns an imported function. Then that imported function accepts a handler, that uses context, as an argument.
import { ironSessionUtil } from './anotherUtil'
const checker = () =>
ironSessionUtil(({ req }) => {
const { user } = req.session
if (!user) {
return {
redirect: { destination: '/welcome', permanent: false }
}
}
return { props: { details: user.details } }
})
export default checker
So far I've tried just calling the method but it returns an Async Function.
it('should return props', () => {
const result = checker()
console.log('result >> ', result)
})
This returns:
[AsyncFunction: nextGetServerSidePropsHandlerWrappedWithIronSession]
I've been scouring the web on how to test this. Would you suggest refactors to make this more testable? Or do you know how this can be done in Jest?

Jest says TypeError: (0 , ClassName.functionName) is not a function

I have a js file as follows
import axios from "axios";
export default class SomeService {
static getSomething(id) {
return axios
.get(API_BASE_URL + API_URL_Something, {
params: { id: id},
})
.then((result) => {
return result.data;
})
.catch((error) => {
throw error;
});
}
}
And I have a test.js using jest as follows
import axios from "axios";
import { getSomething } from "../SomeService.js";
jest.mock("axios");
describe("getSomething", () => {
describe("when API call is successful", () => {
it("should return some details", () => {
// given
const someDetails = [
{ id: 1, name: "Something 1" },
{ id: 2, name: "Something 2" },
];
axios.get.mockResolvedValueOnce(someDetails);
// when
const result = getSomething(123); // ERROR THROWN HERE
// then
let url = API_BASE_URL + Constants.API_URL_Something;
expect(axios.get).toHaveBeenCalledWith(`${url}`);
expect(result).toEqual(someDetails);
});
});
describe("when API call fails", () => {
it("should return empty entity details", () => {
// ...
});
});
});
But when I run npm test I get this error
TypeError: (0 , _SomeService.getSomething) is not a function
I have tried making the function non static (I shouldn't have to), tried exporting it (couldn't get the syntax right perhaps), but can't get it to quite work. Other similar posts do not solve my issue either. What am I doing wrong?
From what I can see in your code you are exporting the class SomeService, not its internal getSomething method. So surely you need to change your test code to something like the following:
import axios from "axios";
import { getSomething } from "../SomeService.js";
jest.mock("axios");
describe("getSomething", () => {
describe("when API call is successful", () => {
it("should return some details", () => {
//...
// when
const service = new SomeService();
const result = service.getSomething(123);
// ...

Jest testing service call responses witth promises, useEffect and useState hooks

I'm having some difficulty testing this useEffect in jest. The following piece of code is within a react functional component and I want to return some mock values when the serviceFn is called. The returned data is written back to state.
//from service-factory.js
const serviceFn = () => (
({ personId }) => (
ionXHR.request(
`/persons/${personId}/`,
'GET',
null,
'json',
)
)
);
//from Component.jsx
const service = useRef(serviceFn());
useEffect(() => {
service.current({ personId:123456 }).then((response) => {
if (response.data) {
setData(response.data);
setLoadingState('SUCCESS');
} else {
setLoadingState('FAILED');
}
});
}, [personId]);
I have the following, but not sure what else I would need.
function mockReturnFn() { return 'Test'; }
const wrapper = mount(<Component/>);
const somethingSpy = jest.spyOn(wrapper, 'serviceFn').mockImplementation(mockReturnFn);
Update:
So, I think I'm getting close.
In my test file I had to import the function
import { serviceFn } from './service-factory';
jest.mock('./service-factory', () => ({ serviceFn : jest.fn() }));
In my test I have
serviceFn.mockImplementation(() => Promise.resolve('test1234'));
The issue now with this is I am getting service.current is not a function
I tried to do this but now getting _serviceFactory.serviceFn.mockImplementation is not a function
jest.mock('./service-factory', () => (
{
serviceFn: {
current: jest.fn(),
},
}
));
serviceFn is factory function, it returns a function that makes requests.
Considering it's named export, it should be initially stubbed as:
jest.mock('./service-factory', () => {
const mockService = jest.fn();
return {
__esModule: true,
mockService,
serviceFn: jest.fn().mockReturnValue(mockService)
}
});
mockService is exposed and allows to mock specific responses:
mockService.mockResolvedValue({ data: ... });
Since it's basic wrapper over ionXHR, it's also possible to mock responses one level higher on ionXHR.request calls.

How can I do a jest test in this function in React axios?

I guys I created a service in React and I need to test this part of the service, I'm using axios and Jest to do this.
I have the next code in React :
import axios from 'axios';
import Endpoints from './endpoints';
const baseUrl = Endpoints.getBackendEndpoint();
export const validateName = (nameObject, callback) => {
axios.post(`${baseUrl}/validateName`, {...nameObject})
.then(response =>{
response.data
})
.then(data => callback(data));
};
I don't need return the promise because all the work is doing by the callback() function.
This is the code that I have in Jest:
mport moxios from 'moxios';
import * as service from '../service';
import mockResponses from './service.test.json';
import Endpoints from '../endpoints';
const validateObjName = {
Id: 1,
Name: 'Bob',
}
beforeEach(() => {
const baseUrl = Endpoints.getBackendEndpoint();
moxios.stubRequest(
`${baseUrl}/validateName`,
{ ...validateObjName },
{
status: 200,
response: mockResponses.validateForm,
}
);
});
afterEach(() => {
moxios.uninstall();
});
it('validateName()', () => {
service.validateName(validateObjName, jest.fn());
});
It works, but still need to increase the Branch coverage.
Thanks for you help guys :D
To get code coverage the code has to run while a test is running so you will want to return the Promise so you can await it in your test so the then callbacks run during your test.
Also, you can simplify validateName to this:
import axios from 'axios';
import Endpoints from './endpoints';
const baseUrl = Endpoints.getBackendEndpoint();
export const validateName = (nameObject, callback) => {
return axios.post(`${baseUrl}/validateName`, { ...nameObject })
.then(response => callback(response.data));
};
In your test you need to install moxios in your beforeEach and pass the mock response as the second parameter to moxios.stubRequest.
Then use an async test function and await the Promise returned by validateName:
import moxios from 'moxios';
import * as service from '../service';
import mockResponses from './service.test.json';
import Endpoints from '../endpoints';
const validateObjName = {
Id: 1,
Name: 'Bob',
}
beforeEach(() => {
moxios.install(); // install moxios
const baseUrl = Endpoints.getBackendEndpoint();
moxios.stubRequest(
`${baseUrl}/validateName`,
{
status: 200,
response: mockResponses.validateForm
}
); // response is the second argument
});
afterEach(() => {
moxios.uninstall();
});
it('validateName()', () => {
service.validateName(validateObjName, jest.fn());
});
it('validateName()', async () => { // use an async test function
const spy = jest.fn();
await service.validateName(validateObjName, spy); // await the Promise
expect(spy).toHaveBeenCalledWith(mockResponses.validateForm); // Success!
});
That should give you a working test and 100% code coverage.

Testing fetch action in react/redux app

Im starting with unit testing and Jest. What I want is to test the action's response after fetching some resources from the db.
This is the action code:
export function loadPortlets() {
return function(dispatch) {
return portletApi.getAllPortlets().then(response => {
dispatch(loadPortletsSuccess(response));
dispatch(hideLoading());
}).catch(error => {
dispatch({ type: null, error: error });
dispatch(hideLoading());
throw(error);
});
};
}
This code is fetching data from:
static getAllPortlets() {
return fetch(`${API_HOST + API_URI}?${RES_TYPE}`)
.then(response =>
response.json().then(json => {
if (!response.ok) {
return Promise.reject(json);
}
return json;
})
);
}
And this is the test:
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import fetch from 'isomorphic-fetch';
import fetchMock from 'fetch-mock';
import * as actions from '../portletActions';
import * as types from '../actionTypes';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
const mockResponse = (status, statusText, response) => {
return new window.Response(response, {
status: status,
statusText: statusText,
headers: {
'Content-type': 'application/json'
}
});
};
describe('async actions', () => {
afterEach(() => {
fetchMock.reset();
fetchMock.restore();
})
it('calls request and success actions if the fetch response was successful', () => {
window.fetch = jest.fn().mockImplementation(() =>
Promise.resolve(mockResponse(200, null, [{ portlets: ['do something'] }])));
const store = mockStore({ portlets: []});
return store.dispatch(actions.loadPortlets())
.then(() => {
const expectedActions = store.getActions();
expect(expectedActions[0]).toContain({ type: types.LOAD_PORTLETS_SUCCESS });
})
});
});
And this is the result of running the test:
FAIL src\actions\__tests__\portletActions.tests.js
● async actions › calls request and success actions if the fetch response was successful
expect(object).toContain(value)
Expected object:
{"portlets": [// here an array of objects], "type": "LOAD_PORTLETS_SUCCESS"}
To contain value:
{"type": "LOAD_PORTLETS_SUCCESS"}
at store.dispatch.then (src/actions/__tests__/portletActions.tests.js:56:34)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
In the redux docs for this example (https://redux.js.org/recipes/writing-tests), they receive a result containing only the action types executed, but I'm getting the real data and the action inside the array.
So I'm not sure if the code is wrong, or the test, or both!
Thanks in advance, any help is highly appreciated!
You're testing too much with this unit test. I see you are using thunks it looks like so you can change your fetch to be passed as a module to the thunk and do something like this. I used jasmine but it's basically the same thing. You don't want to mock your store here just the action and dispatch. The point of the unit test should be to test the async action, not to test getting real data from the db or redux store interactions so you can stub all that out.
For reference configureStore would look like this...
const createStoreWithMiddleware = compose(
applyMiddleware(thunk.withExtraArgument({ personApi }))
)(createStore);
And the test case...
it('dispatches an action when receiving', done => {
const person = [{ firstName: 'Francois' }];
const expectedAction = {
type: ActionTypes.RECEIVED,
payload: {
people,
},
};
const dispatch = jasmine.createSpy();
const promise = Q.resolve(person);
const personApi = {
fetchPerson: jasmine
.createSpy()
.and.returnValue(promise),
};
const thunk = requestPerson();
thunk(dispatch, undefined, { personApi });
promise.then(() => {
expect(dispatch.calls.count()).toBe(2);
expect(dispatch.calls.mostRecent().args[0]).toEqual(expectedAction);
done();
});
});

Resources