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.
Related
I have a component that looks like this:
<TestComponent refetch={fn} />
Within TestComponent, I have a save button that fires off a mutation:
const handleSaveClick = () => {
const reqBody: AddHotelRoomDescription = {...}
addHotelRoomDescriptionMutation(reqBody, {
onSuccess: () => {
closeDrawer();
handleRefetch();
}
})
}
Within my test file for this component, I am trying to ensure handleRefetch gets called:
it('successfully submits and calls the onSuccess method', async () => {
const mockId = '1234'
nock(API_URL)
.post(`/admin/${mockId}`)
.reply(200);
const user = userEvent.setup();
const editorEl = screen.getByTestId('editor-input');
await act( async () => { await user.type(editorEl, 'Updating description'); });
expect(await screen.findByText(/Updating description/)).toBeInTheDocument();
const saveButton = screen.getByText('SAVE');
await act( async () => { await user.click(saveButton) });
expect(mockedMutate).toBeCalledTimes(1);
await waitFor(() => {
expect(mockedHandleRefetch).toBeCalledTimes(1); <-- fails here
})
})
I am not sure how to proceed here. I know I can test useQuery calls by doing:
const { result } = renderHook(() => useAddHotelRoomDescription(), { wrapper }
But I think this is for a different situation.
Appreciate the guidance!
I have written a custom hook and inside it's useEffect function I am calling an API and set the result into state. Here is my custom hook:
export const useGetUsers = (searchParams?: any | undefined, userId?: string) => {
const [users, setUsers] = useState<{
data: readonly any[] | null;
loading: boolean;
}>({
data: [],
loading: true,
});
const parsedSearchParams = {
limit: 100,
...(searchParams || {}),
};
const searchParamStr = `?${makeQueryStringFromObject(parsedSearchParams)}`;
useEffect(() => {
userRequest('users', 'get', null, searchParamStr)
.then(result => {
setUsers({
data: result?.data,
loading: false,
});
})
.catch(() => {
setUsers({
data: null,
loading: false,
});
});
}, [userId, searchParamStr]);
return { users, setUsers };
};
I want my test to get through .then(). but for some reason it does not. here is my test:
test('when the call is a success', async () => {
const spy = jest.spyOn(ES, 'userRequest');
const returnPromise = Promise.resolve({data: ['a']})
ES.userRequest = jest.fn(() => returnPromise);
const { result, waitFor} = renderHook(() => useGetUsers());
await act(() => returnPromise)
await waitFor(() => expect(spy).toHaveBeenCalled())//this fails
});
here is another try and change I made in my test, but no luck:
test('when the call is a success', async () => {
jest.mock('src/..', () => ({
...jest.requireActual('src/..'),
userRequest: jest
.fn()
.mockImplementation(() => new Promise(resolve => resolve({data: ['a']}))),
}));
const { result, waitFor} = renderHook(() => useGetUsers());
await waitFor(() => expect(ES.userRequest).toHaveBeenCalled())
});
P.S. when I mock userRequest, I expect to have the return value as I mocked. but it fails. it goes to .catch instead
I tried to use waitForNextUpdate, but no luck. I would appreciate your help
This works for me:
import { renderHook, waitFor } from '#testing-library/react';
import { useGetUsers } from '../useGetUsers';
import * as utils from '../useGetUsersUtils';
it('should work', async () => {
const mockUserRequest = jest.spyOn(utils, 'userRequest');
renderHook(() => useGetUsers());
await waitFor(() => expect(mockUserRequest).toHaveBeenCalled())
});
I am not sure where is the userRequest placed in your code. As you can see from my import it is in different file then the hook.
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!
})
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);
});
I am trying to test if a button dispatches an action but instead of an action type I get []. The functionality works completely fine outside the test this is why I don't understand why the test fails.
My test:
const mockStore = configureStore();
const store = mockStore({});
describe('buttonUploadWorks', () => {
it('checks if the button for upload dispatches an action', () => {
const wrapper = shallow(
<Provider store = {store}>
<DropzoneComponent.WrappedComponent/>
</Provider>).dive();
const uploadButton = wrapper.find('button').at(0);
uploadButton.simulate('onClickUpload');
const expectedAction = UPLOAD_STARTING;
wrapper.setState({ accepted: ['abc'] });
const action = store.getActions();
expect(action).to.equal(expectedAction);
});
});
My actions:
export const uploadStarting = () => {
return {type: UPLOAD_STARTING};
};
export const uploadSuccess = uploadMessage => {
return {type: UPLOAD_SUCCESS, uploadMessage};
};
export const uploadFail = error => {
return {type: UPLOAD_FAIL, error};
};
export function tryUpload (file) {
const formData = new FormData();
formData.append('file', file);
return (dispatch) => {
dispatch(uploadStarting());
axios.post(filesEndpoint, formData).then(function (response) {
dispatch(uploadSuccess(response));
}).catch(function (error) {
dispatch(uploadFail(error));
});
};
};
And the button:
<button className='buttonUpload' onClick={() => { this.state.accepted.length > 0 ? this.onClickUpload().bind(this) : alert('No files presented'); }}>UPLOAD</button>
onClickUpload() {
this.props.dispatch(tryUpload(this.state.accepted[0]));
localStorage.clear();
this.setState({accepted: []});
this.props.history.push(searchPath);
}
That's happening because setState({accepted:[]}) triggers before this.props.dispatch(tryUpload(this.state.accepted[0])) you could fix it binding dispatch function inside a promise function and then calling the setState function.
JavaScript Promises