Jest: How to mock a redux thunk calling another thunk? - javascript

I have the following redux thunk:
function thunkOne() {
return dispatch => {
callToApi().then(() => {
dispatch(someNonThunkAction());
dispatch(thunkTwo());
});
}
}
function thunkTwo() {
return dispatch => {
anotherCallToApi.then(dispatch(someOtherAction()));
}
}
I would like to test only thunkOne and mock thunkTwo so that it doesn't get executed when I test thunkOne.
I tried to do this but it doesn't work:
import * as someActions from './actions';
it ('thunkOne should dispatch someNonThunkAction and thunkTwo', () => {
someActions.thunkTwo = jest.fn();
const expectedActions = [
{ type: SOME_NON_THUNK_ACTION, data: {} }
],
store = mockStore(initialState);
store.dispatch(someActions.thunkOne()).then(() => {
expect(store.getActions()).toEqual(expectedActions);
expect(someActions.thunkTwo).toHaveBeenCalled();
});
someActions.thunkTwo.mockRestore();
});
I get the following error when I run this test:
[ERROR] Actions must be plain objects. Use custom middleware for async actions.
How do I mock thunkTwo and test thunkOne only?

Related

How to test an imported function returning a Promise in Jest?

I have this very simple function, and I must write a test. The goal is to fulfill the coverage threshold.
import { lambdaPromise } from '#helpers';
export const main = async event => lambdaPromise(event, findUsers);
The lambdaPromise() function returns a Promise. I am trying to mock it, then tell if it was called. Here's what I have:
import { main, findUsers } from '../src/lambdas/user/findUsers';
import { lambdaPromise } from '#helpers';
const mockEvent = {
arguments: {
userDataQuery: {
email: 'johndoe#whatever.com'
}
}
};
const mockLambdaPromise = jest.fn();
jest.mock('#helpers', () => ({
lambdaPromise: jest.fn().mockImplementation(() => mockLambdaPromise)
}));
describe('findUsers', () => {
it('should have a main function', async () => {
const mockPromise = main(mockEvent);
expect(mockPromise).toBeInstanceOf(Promise);
expect(mockLambdaPromise).toBeCalledWith(mockEvent, findUsers);
});
});
Now mockLambdaPromise never gets called. How to fix that?
Your mock returns a function, but you didn't call that function. The following makes it pass.
jest.mock("./helpers", () => ({
lambdaPromise: jest
.fn()
.mockImplementation((a, b) => mockLambdaPromise(a, b)),
}));
The complexity of that mock can be reduced by just mocking the resolved value with a spy:
import { main, findUsers } from "./findUsers";
import * as helpers from "./helpers";
describe("findUsers", () => {
it("should have a main function", async () => {
const spy = jest.spyOn(helpers, "lambdaPromise").mockResolvedValue();
await main(mockEvent);
expect(spy).toBeCalledWith(mockEvent, findUsers);
});
});

Vue-test-utils: How do I mock the return of an action in VueX?

I'm writing a test for a Vue component which dispatches to a module store to perform an action and use the result from it.
The action makes a call to our API so I don't want to run the test with that action, but instead mock it and return some dummy data to see that the rest of the method flow works.
So in my test I add a mock store, with a mocked action which just returns hardcoded data, with the intent to see that the component method getData() sets the response of the action to the data.
This doesn't seem to work however and instead it seems as if the real action is called. How do I set it up so that I can avoid calling the real actions and instead use the ones I create for the tests?
Component method, simplified:
methods: {
async getData() {
const response = this.$store.dispatch("global/getDataFromAPI")
if (!response) return
this.$data.data = {...response.data}
}
}
Test code, simplified:
describe('Component.vue', () => {
let localVue;
let vuetify;
let wrapper;
let store;
beforeEach(() => {
localVue = createLocalVue();
localVue.use(Vuex)
vuetify = new Vuetify();
let globalActions = {
getDataFromAPI: async () => {
return {
status: 200,
data: {
information1: "ABC",
information2: 123,
}
}
}
}
store = new Vuex.Store({
modules: {
global: {
actions: globalActions,
namespaced: false
},
}
})
wrapper = mount(Component, {
localVue,
vuetify,
attachTo: div,
mocks: {
$t: () => { },
$store: store,
},
});
});
it('Data is set correctly', async () => {
await wrapper.vm.getData();
const dataInformation1 = wrapper.vm.$data.data.information1;
expect(dataInformation1).toBe("ABC")
});
First, if you want to mock the Vuex Store you don't need to call localVue.use(Vuex). You should call localVue.use(Vuex) only if you are going to use real Vuex Store in test. And if you are going to you must pass store object along with localVue and another arguments, not in mocks property.
Second, to mock your action you can mock store's dispatch method like this:
mocks: {
$store: {
dispatch: () => { dummyData: 'dummyData' }
}
}

Jest: Vue Component can't find mocked function

I'm mocking a ES6 class which is used inside my Vue Component:
export default class DataUploadApi {
// Get uploaded files
static async getUploadedFiles() : Promise<Object> {
return WebapiBase.getAsync({uri: DATA_UPLOAD_ENPOINTS.FILES});
}
}
I've been following along with this document, but I think my syntax is slightly off with my mock:
import { mount } from '#vue/test-utils';
import DataUploadApi from '../webapi/DataUploadService';
import FileDownloadList from '../components/file-download-list.vue';
const mockGetUploadedFiles = jest.fn().mockResolvedValue({json: JSON.stringify(uploadedFilesObj)});
jest.mock('../webapi/DataUploadService', () => jest.fn().mockImplementation(() => ({getUploadedFiles: mockGetUploadedFiles})));
describe('file-download-list component', () => {
beforeEach(() => {
// #ts-ignore
DataUploadApi.mockClear(); // https://stackoverflow.com/a/52707663/1695437 dont use # imports on mocks.
mockGetUploadedFiles.mockClear();
});
describe('renders correct markup:', () => {
it('without any uploaded files', () => {
const wrapper = mount(FileDownloadList, {});
expect(wrapper).toMatchSnapshot();
});
});
});
This test passes. However, in the snapshot I can see that the API called failed with this error message:
<p>
_DataUploadService.default.getUploadedFiles is not a function
</p>
What have I done wrong with the function mock? Thanks in advance!
There seemed to be a few issues with my code:
Mocking the API
Using an internal mockImplementation seems to cause issues, and is not required if you don't need the additional mock functionality.
jest.mock('#/apps/gb-data/webapi/DataUploadService', () => ({
getUploadedFiles() {
return Promise.resolve({ uploaded_files: {} });
},
}));
Changes to the test
Both flushPromises and nextTick are required.
it('with uploaded files', async () => {
const wrapper = mount(FileDownloadList, {
stubs: fileDownloadListStubs,
});
await flushPromises();
await wrapper.vm.$nextTick();
expect(wrapper).toMatchSnapshot();
});

Redux Mock Store giving 'Actions must be plain objects. Use custom middleware for async actions.'

I am trying to test some async code in my React app using redux-mock-store.
const configureMockStore = require('redux-mock-store').default;
const thunk = require("redux-thunk").default;
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
const dummy = () => {
// Mock Ajax call
return new Promise((resolve, reject) => {
setTimeout(() => resolve({data: 'data'}), 200)
})
};
describe("Redux Mock Store", () => {
it("Test Dummy Ajax call", () => {
const expectedActions = [
{ type: "SUCCESS", payload: "success" },
{ type: "FAILURE", error: { Error: "Error" } }
];
const store = mockStore({});
store.dispatch(dummy().then(() => {
expect(store.getActions()).toEqual(expectedActions)
}).catch(error => { console.log(error) }))
});
});
I am using Jest to run this test. I get the following error when running above test Actions must be plain objects. Use custom middleware for async actions. What's wrong here?
The problem is that you are using redux-thunk middleware but you are not dispatching any action once your promise resolves (you can check how to define an action creator that uses redux-thunk in the documentation).
So, you need to define an action creator that uses your dummy ajax request and dispatches an action once it has finished:
const dummy = () => {
// Mock Ajax call
// Note that you are not capturing any error in here and you are not
// calling the reject method, so your *catch* clausule will never be
// executed.
return new Promise((resolve, reject) => {
setTimeout(() => resolve({ data: 'success' }), 200);
});
};
const actionCreator = () => (dispatch) => {
return dummy()
.then(payload => dispatch({ type: 'SUCCESS', payload }))
.catch(error => dispatch({ type: 'FAILURE', error }));
};
Note how the action creator receives a parameter dispatch (that is provided by redux-thunk middleware) and we use that function to dispatch our actions (that are simple objects).
Once you call your action creator with the correct parameters, you should return your promise in the it so that it waits until the promise has resolved and executes the expects inside the then statement:
describe('Redux Mock Store', () => {
it('Test Dummy Ajax call', () => {
const expectedActions = [
{ type: 'SUCCESS', payload: { data: 'success' } },
];
const store = mockStore({});
return store.dispatch(actionCreator()).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
});
Also, take into account that in your initial test you are expecting two actions to be dispatched, but you are only calling your action creator once. You should test the failure case in another it.
You can see the solution working here.

Mocking a function inside of a Redux action

I am writing tests for my redux actions. In one of my complex actions I have a function, e.g. aRandomFunction that I want to mock. How do I add write a test that mocks a function that is used inside of the fetchAction? Thanks! You can see the example below.
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
jest.mock('../../api/handleError');
jest.mock('../../api/handleResponse');
let store;
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
beforeEach(() => {
store = mockStore({});
fetchMock.restore();
});
const aRandomAction = () => ({
type: "RANDOM_ACTION",
})
const aRandomFunction = (data, dispatch) => {
if (data.isTrue) {
dispatch(aRandomAction);
}
};
export const fetchAction = () => {
return (dispatch) => {
dispatch(requestAction());
return fetch('sampleApi/foo')
.then(response => handleResponse(response))
.then((json) => {
aRandomFunction(json.data, dispatch);
dispatch(receiveAction(json.data));
})
.catch(error => handleError(error));
};
};
describe('testing the fetch Action', () => {
test('testing the fetch action', () => {
const expectedActions = [
{ type: "REQUEST_ACTION" },
{ type: "RECEIVE_ACTION", data: "payload" },
];
return store.dispatch(fetchAction()).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
});
You cannot mock aRandomFunction in this case, because it's not exported. Although this is not explicitly said in Jest's documentation, please note in the examples that only importable code can be mocked with Jest. You could focus on testing the final outcome of fetchAction, and what happens in the middle wouldn't matter. It's completely fine not to test it because it's implementation details, that is, it only defines the means used by fetchAction to achieve its goal, which could change over time and break your tests, even if the goal of fetchAction keeps being correctly achieved.
But if it's important for you to be able to test aRandomFunction, you will have to move it to an external file, and export it from there. After doing that, you'll be able to mock it in the same way that you're mocking other dependencies, such as handleError and handleResponse. You can even define a mock implementation if it's necessary for your test case, for example:
random-function.js
const aRandomAction = () => ({
type: "RANDOM_ACTION",
});
const aRandomFunction = (data, dispatch) => {
if (data.isTrue) {
dispatch(aRandomAction());
}
}
export default aRandomFunction;
your-test-case.spec.js (place this along with your test case from the example in the question)
import aRandomFunction from "./random-function";
jest.mock("./random-function");
aRandomFunction.mockImplementation((data, dispatch) => {
dispatch({ type: "MOCK_ACTION" );
});

Categories

Resources