I have a following action, it’s an axios call with curried dispatch.
Note: originally, we wouldn’t need to curry the action call like that, but let’s say there is a situation where I need to make it curried (code below is just a made-up example).
./src/store/Auth/auth.action.ts
import { Dispatch } from 'redux';
import axios from 'axios';
export default {
getUsersData: () => (dispatch: Dispatch) => {
return axios.get('https://jsonplaceholder.typicode.com/users').then((result) => {
dispatch({
type: 'SET_USERS',
payload: result
});
return result;
})
.catch((e) => {
console.error(e);
});
},
}
./src/screens/Testing/index.tsx
import React, { useCallback } from 'react';
import { View, Button } from 'react-native';
import { useDispatch } from 'react-redux';
import Action from '../../store/Auth/auth.action';
const useAppDispatch = (action: any) => {
const dispatch = useDispatch();
return useCallback((param?: any, callback?: any) => dispatch(action(param, callback)), [dispatch, action]);
};
const Testing = () => {
const getUsersDataAction = useAppDispatch(Action.getUsersData);
return (
<View>
<Button
title='hit me'
testID='hit_me_id'
onPress={ () => getUsersDataAction() }
/>
</View>
);
};
export default Testing;
action.test.js
import * as React from 'react';
import * as ReactRedux from 'react-redux';
import axios from 'axios';
import { render, fireEvent, waitFor } from '@testing-library/react-native';
import { Testing } from '../../src/screens';
describe('Get users data', () => {
it('should return correct payload', async () => {
const mockDispatch = jest.fn();
jest.spyOn(ReactRedux, 'useDispatch').mockReturnValue(mockDispatch);
axios.get.mockResolvedValue({});
const screen = render(
<Testing />
);
await waitFor(() => {
expect(screen.getByTestId('hit_me_id')).toBeTruthy();
});
await waitFor(() => {
fireEvent.press(screen.getByTestId('hit_me_id'));
});
expect(mockDispatch).toHaveBeenCalledTimes(1);
expect(axios.get).toHaveBeenCalledWith('https://jsonplaceholder.typicode.com/users');
});
});
I feel the mock declaration is still not right.
const mockDispatch = jest.fn();
jest.spyOn(ReactRedux, 'useDispatch').mockReturnValue(mockDispatch);
Have tried with some other ways like:
const mockDispatch = () => jest.fn();
const mockDispatch = jest.fn(() => () => null);
jest.spyOn(ReactRedux, 'useDispatch').mockReturnValue(mockDispatch();
jest.spyOn(ReactRedux, 'useDispatch').mockReturnValue(() => mockDispatch();
but still not found the solution.
Does anyone know how to mock a curried function with Jest?
Many thanks in advance!
2
Answers
First, I suggest you to spy the axios request too in this way:
Then, since is the
toHaveBeenCalledWith
function that is failing, meaning thatexpect(mockDispatch).toHaveBeenCalledTimes(1)
is passing, the expectation here is that the arguments are not resolved (called) since we are using a mocked function fordispatch
.In this scenario I suggest you to mock the implementation of
dispatch
to call the action:Disclaimer: I haven’t used Redux for a long time, so my insight about the Redux part might not be 100% accurate regarding the part of calling actions / action creators.
The solution to the question
It seems that the missing part is mocking the behaviour of your
mockDispatch
function.TL;DR: Mock your
mockDispatch
implementation so it calls thegetUsersData
function from your actions.Explanation:
This is what your code does:
getUsersDataAction
function with youruseAppDispatch
hook. This returns a function that will be calledon press.
getUsersDataAction
is called.dispatch
function by callinguseDispatch
.dispatch
function is called with your action, what does whatever it has to do (callingaxios
in this case).This is what your test code does:
getUsersDataAction
is called.useDispatch
is called but you’ve mocked its return value,mockDispatch
is returned.mockDispatch
is called.mockDispatch
is justjest.fn()
; no mock resolved values or mock implementations have been defined for it (or not an implementation that does anything in particular in your later approaches), so nothing is actually done in this call.getUsersData
from your actions file (the one who actually callsaxios
) is never run because nothing calls it.My suggestion is to mock the implementation of
mockDispatch
in a way that actually calls your action:My approach as an engineer
Don’t test everything as a whole. You can assume that Redux is a well tested library, so call the parts actually done by you in a separate way:
mockDispatch
is called).I understand you can be interested in a more integrated approach that proves both, but I’d leave that for an e2e test. Integration tests made with tools more focused on unit tests (what is Jest’s case) that also selectively mocks in-between stuff are harder to reason about. If a test is going to be expensive to read and maintain, let better make it an e2e test.