How to test a dispatch function? - reactjs

Hello I'm trying to test this function with the return of the dispatch in how many times it have been called, but really don't know how to do it correctly in order to call dispatch
export const mapDispatchToProps = (dispatch) => {
return {
hideSidebar: () => {
dispatch(hideSidebar)
},
updateUnit: (unitObject) => {
dispatch(settingsActions.updateArray(unitObject))
}
}
}
I have these test
describe('mapDispatchToProps', () => {
test('test', () => {
const dispatch = jest.fn(() => Promise.resolve())
mapDispatchToProps(dispatch)
expect(dispatch).toHaveBeenCalledTimes(2)
})
})
Any suggestions?

Create a dispatch mock function and pass it to mapDispatchToProps.
Then call the functions defined on the result.
You can use something like toHaveBeenCalledWith to verify that the correct action was dispatched:
// Stubs for hideSidebar and settingsActions.updateArray
const hideSidebar = { type: 'hide-side-bar' };
const settingsActions = { updateArray: u => ({ type: 'update-unit', payload: u })};
export const mapDispatchToProps = (dispatch) => {
return {
hideSidebar: () => {
dispatch(hideSidebar)
},
updateUnit: (unitObject) => {
dispatch(settingsActions.updateArray(unitObject))
}
}
}
test('mapDispatchToProps', () => {
const dispatch = jest.fn();
const map = mapDispatchToProps(dispatch);
map.hideSidebar();
expect(dispatch).toHaveBeenCalledWith({ type: 'hide-side-bar' }); // Success!
map.updateUnit({ theKey: 'theVal' });
expect(dispatch).toHaveBeenCalledWith({ type: 'update-unit', payload: { theKey: 'theVal' } }); // Success!
})

Related

Jest test createasyncthunk function that has dispatch in it

I have a createAsyncThunk function as such
export const startup = createAsyncThunk<
void,
void,
{
dispatch: AppDispatch;
}
>("startup", async (_params, { dispatch }) => {
const appConfigs = await retrieveStoredAppConfiguration();
if (appConfigs?.tenantConfig && appConfigs?.clientRegistration) {
const configs: InitAppConfigs = {
tenantConfig: appConfigs.tenantConfig,
clientRegistration: appConfigs.clientRegistration,
};
await initializeApp(configs, dispatch);
dispatch(appConfigurationSlice.actions.initialize(appConfigs));
}
});
So I have a unit test as such
describe("AppStartupThunk", () => {
it("should dispatch AppStartupThunk successfully", async () => {
const retrieveStoredAppConfigurationSpy = jest
.spyOn(retrieveStoredAppConfiguration, "retrieveStoredAppConfiguration")
.mockImplementation(() => {
return Promise.resolve(appConfigs);
});
const result = await store.dispatch(initApplicationStartup());
expect(true).toBeTruthy();
expect(retrieveStoredAppConfigurationSpy).toHaveBeenCalled();
});
});
My test covers all the lines except for when dispatch is invoked in the createAsyncThunk. Is there any way I can cover that dispatch call. I'm not sure how to do it with createAsyncThunk.

How to cover lines in mapDispatchToProps with Jest?

My question is how do we cover these lines in jest?
export const mapDispatchToProps = dispatch => {
return {
submitClaimsForm: form => {
dispatch(submitClaimsForm(form));
}
};
};
In my component this is what the redux connected area looks like:
export function mapStateToProps(state) {
return {
formNonMember: state.form,
submissionSuccess: state.claimSubmission.submissionSuccess
};
}
export const mapDispatchToProps = dispatch => {
return {
submitClaimsForm: form => {
dispatch(submitClaimsForm(form));
}
};
};
let AdditionalDetailsFormConnect = reduxForm({
form: 'AdditionalDetails',
destroyOnUnmount: false
})(AdditionalDetailsForm);
export default connect(
mapStateToProps,
mapDispatchToProps
)(AdditionalDetailsFormConnect);
And this is how the dispatched action is used:
onSubmit() {
this.props.submitClaimsForm(this.props.formattedForm);
}
Next this is what the actual action looks like:
import {postClaimsForm} from '../shared/services/api';
export const Actions = {
SET_SUBMISSION_STATUS: 'SET_SUBMISSION_STATUS'
};
export const submitClaimsForm = form => dispatch => {
return postClaimsForm(form)
.then(res => {
// console.log('promise returned:', res);
return dispatch({
type: Actions.SET_SUBMISSION_STATUS,
submissionSuccess: true
});
})
.catch(error => {
// console.log('error returned:', error);
return dispatch({
type: Actions.SET_SUBMISSION_STATUS,
submissionSuccess: false
});
});
};
What I've tried so far:
it('mapDispatchToProps works as expected', () => {
const actionProps = mapDispatchToProps({
submitClaimsForm: jest.fn()
});
actionProps.submitClaimsForm();
expect(submitClaimsForm).toHaveBeenCalled();
});
But this errors and tells me that dispatch is undefined.
I also have this test, which passes, it tells me that submitClaimsForm has been called, but it just covers the lines for onSubmit:
it('onSubmit is called on submit', function() {
const spyOnSubmit = jest.spyOn(wrapper.instance(), 'onSubmit');
const mockHandleSubmit = jest.fn(wrapper.instance().onSubmit);
const submitClaimsForm = jest.fn(wrapper.instance().submitClaimsForm);
wrapper.setProps({
handleSubmit: mockHandleSubmit,
submitClaimsForm
});
wrapper.find('MyForm').simulate('submit');
expect(mockHandleSubmit).toHaveBeenCalled();
expect(spyOnSubmit).toHaveBeenCalled();
expect(submitClaimsForm).toHaveBeenCalled(); // <--
});
The reason your mapDispatchToProps works as expected test fails is because mapDispatchToProps expects a dispatch function to be passed in, not the map itself (that's what mapDispatchToProps returns).
This should work:
jest.mock('./actions');
import * as actions from './actions';
it('mapDispatchToProps calls the appropriate action', async () => {
// mock the 'dispatch' object
const dispatch = jest.fn();
const actionProps = mapDispatchToProps(dispatch);
const formData = { ... };
actionProps.submitClaimsForm(formData);
// verify the appropriate action was called
expect(actions.submitClaimsForm).toHaveBeenCalled(formData);
});

test mapDispatchToProps async actions

I am trying to test my mapDispatchToProps function when an asyncronous function is dispatched. I have read Dan Abramov's suggestions on how to test mapDispatchToProps and I am trying to test my code as such.
I am getting the error...
TypeError: Cannot read property 'then' of undefined
Here is my test...
describe("mapDispatchToProps", () => {
const dispatchSpy = jest.fn();
const {signupUser} = mapDispatchToProps(dispatchSpy);
it("should dispatch signupActions.signupUser()", () => {
// mockAxios.onPost(endpoints.SIGNUP,{})
// .reply(200,'test_success');
// tried with and without mockAxios
signupUser({})
const spyLastCall = dispatchSpy.mock.calls[0][0];
expect(spyLastCall)
.toEqual(signupActions.signupUser({}));
})
})
The function that I want to test...
export const mapDispatchToProps = dispatch => {
return { signupUser: (user) => {
dispatch(signupActions.signupUser(user))
.then((response) => {
// do something on success
}, (error) => {
// do something on failure
})
}
}
I have already tested signupActions.signupUser and I know that it returns a promise. Here is the code...
export function signupUser(user) {
return (dispatch) => {
return dispatch(rest.post(SIGNUP,user))
.then((response) => {
return Promise.resolve(response);
},(error) => {
return Promise.reject(error)
}
)
}}
What am I doing wrong?
Ps: I also tried:
const dispatchSpy = jest.fn().mockImplementation( () => {
return p = new Promise((reject,resolve) => {
resolve({})
})
}
with the same result
For anyone who is interested, I ended up using mergeProps which has made my tests a lot cleaner. Now I have...
export const mapDispatchToProps = dispatch => {
return { dispatchSignupUser: (user) => {
dispatch(signupActions.signupUser(user))
}
}
export const mergeProps = (propsFromState,propsFromDispatch,ownProps) => {
return {
signupUser: (values) => {
return propsFromDispatch.dispatchSignupUser(values)
.then(() => { // do something on success },
() => { // do something on failure})
}
}
and I test them separately...
describe("signup", () => {
/// ... ownProps and propsFromState declared here
const dispatchSpy = jest.fn((x) => {});
const {
dispatchSignupUser,
} = mapDispatchToProps(dispatchSpy);
const signupUser = mergeProps(propsFromState,propsFromDispatch,ownProps);
describe("mapDispatchToProps", () => {
it("should dispatch signup user on dispatchSignupUser", () => {
const spyOn = jest.spyOn(signupActions,'signupUser');
dispatchSignupUser({test: "test"});
expect(spyOn).toHaveBeenCalledWith({test: "test"});
})
})
describe("mergeProps", () => {
it("should do something on success", () => {
propsFromDispatch.dispatchSignupUser jest.fn().mockImplementation((x) => {
return new Promise((resolve,reject) => { return resolve({})} )
});
return signupUser({}).then(() => {
expect(history.location.pathname).toEqual("/signup/thank-you")
}, (error) => {})
})
})
})
Hopefully this is helpful!

How to test default action in Jest?

I was testing redux actions using Jest , When i try to test the default action, it throws an Error
Expected value to equal:
{"payload": {"male": "mancha"}, "type": "actions/change_gender"}
Received:
[Function anonymous]
It seems it sends the function, instead of values.
test change_gender.js
import changeGender, { CHANGE_GENDER } from '../change_gender';
const payload = {
type: CHANGE_GENDER,
payload: {
male: 'mancha'
}
};
describe('actions', () => {
it('should Change the ', () => {
const expectedAction = {
type: payload.type,
payload: payload.payload
};
expect(changeGender('male', 'mancha')).toEqual(expectedAction)
});
});
Action change_gender.js
import toggleToolTip from './toggle_tooltip'; // eslint-disable-line
export const CHANGE_GENDER = 'actions/change_gender';
export default(radioType, type) => (dispatch) => {
dispatch({
type: CHANGE_GENDER,
payload: {
[radioType]: type
}
});
};
You should return the dispatch at change_gender.js:
change_gender.js:
import toggleToolTip from './toggle_tooltip'; // eslint-disable-line
export const CHANGE_GENDER = 'actions/change_gender';
export default(radioType, type) => (dispatch) => {
return dispatch({
type: CHANGE_GENDER,
payload: {
[radioType]: type
}
});
};
As Chen-tai mentioned, returning from the dispatch would help here for testing purposes.
The reason you see [Function] being returned is that your action is a function returning a function.
(radioType, type) => (dispatch) => { ... }
The first set of params, followed by the fat arrow is an anonymous function. That then returns another anonymous function that takes a dispatch function as its arguments. So, if we call the action twice, providing a mock dispatch function, we'll get back the expected action!
const action = (radioType, type) => (dispatch) => {
return dispatch({
type: "CHANGE_GENDER",
payload: {
[radioType]: type
}
});
};
console.log(
action('male', 'mancha')((action) => action)
)
We can then write out test:
Action change_gender.js
import toggleToolTip from './toggle_tooltip'; // eslint-disable-line
export const CHANGE_GENDER = 'actions/change_gender';
export default(radioType, type) => (dispatch) => {
return dispatch({
type: CHANGE_GENDER,
payload: {
[radioType]: type
}
});
};
test change_gender.js:
import changeGender, { CHANGE_GENDER } from '../change_gender';
const payload = {
type: CHANGE_GENDER,
payload: {
male: 'mancha'
}
};
describe('actions', () => {
it('should Change the ', () => {
const expectedAction = {
type: payload.type,
payload: payload.payload
};
expect(changeGender('male', 'mancha')((payload) => payload).toEqual(expectedAction)
});
});

Redux-thunk - dispatch is not a function

I'm having trouble with redux-thunk. It's saying dispatch is not a function inside my action creator, I tried consoling the returned arguments and there is none.
Here goes the code:
Action
export function signUp(data) {
return dispatch => {
console.log(dispatch)
if (data.email === 'email#server.com') {
dispatch(signIn(data, () => {
if (data.type === '2') {
browserHistory.push('/settings/profile')
} else {
browserHistory.push('/')
}
}))
} else {
return {
type: ActionTypes.USER_SIGN_UP__ERROR
}
}
}
}`
mapActionsToProps
const mapActionsToProps = dispatch => ({
signUp (data) {
console.log(dispatch)
dispatch(userActions.signUp(data))
}
})
By the way, you can see I consoled the dispatch function inside the mapActionsToProps, and it is returning as it was supposed to:
function (action) {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
}
Dispatch is not a function, because it's not passed from action creator.
Besides, you should not dispatch any action inside your mapActionsToProps. You just need to bind them to be accessible by connected component.
Your mapActionsToProps
const mapActionsToProps = (dispatch) => {
return {
asyncAction: bindActionCreators(asyncAction, dispatch),
}
}
const Container = connect(mapStateToProps, mapActionsToProps)(Component);
Async action
export const asyncAction = (email) => {
return (dispatch, getState) => {
const state = getState();
dispatch(StartAsync());
return fetch(`${apiUrl}/endpoint?email=${email}`, {
method: 'GET'
})
.then(response => response.json())
.then((result) => dispatch(finishedAsync(result)),
(error) => dispatch(failedAsync(error)))
.catch(e => {
console.log('error:', e);
});
};
};
Then, in your connected component, you can dispatch this action from props.

Resources