I'm new to unit testing Redux-Thunk async actions using Jest.
Here is my code:
export const functionA = (a, b) => (dispatch) => {
dispatch({ type: CONSTANT_A, payload: a });
dispatch({ type: CONSTANT_B, payload: b });
}
How can I test this function using Jest?
You have an example in the Redux docs: http://redux.js.org/docs/recipes/WritingTests.html#async-action-creators
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
const middlewares = [thunk]
const mockStore = configureMockStore(middlewares)
describe('async actions', () => {
it('should dispatch actions of ConstantA and ConstantB', () => {
const expectedActions = [
{type: 'CONSTANT_A', payload: 'a'},
{type: 'CONSTANT_B', payload: 'b'}
]
const store = mockStore({ yourInitialState })
store.dispatch(functionA('a', 'b'))
expect(store.getActions()).toEqual(expectedActions)
})
})
Also, for more more convenience, you can use this module: https://www.npmjs.com/package/redux-thunk-tester
Example:
import React from 'react';
import {createStore, applyMiddleware, combineReducers} from 'redux';
import {reducer} from './example';
import ReduxThunkTester from 'redux-thunk-tester';
import thunk from 'redux-thunk';
const request = (ms) => new Promise((resolve) => {
setTimeout(() => resolve('success response'), ms);
});
const resultRequestAction = (value) => ({ type: SOME_BACKEND_REQUEST, payload: value });
const toggleLoadingAction = (value) => ({ type: TOGGLE_LOADING, payload: value });
const asyncThunkWithRequest = () => async (dispatch) => {
try {
dispatch(toggleLoadingAction(true));
const result = await request(200);
dispatch(resultRequestAction(result));
} finally {
dispatch(toggleLoadingAction(false));
}
};
const createMockStore = () => {
const reduxThunkTester = new ReduxThunkTester();
const store = createStore(
combineReducers({exampleSimple: reducer}),
applyMiddleware(
reduxThunkTester.createReduxThunkHistoryMiddleware(),
thunk
),
);
return {reduxThunkTester, store};
};
describe('Simple example.', () => {
test('Success request.', async () => {
const {store, reduxThunkTester: {getActionHistoryAsync, getActionHistoryStringifyAsync}} = createMockStore();
store.dispatch(asyncThunkWithRequest());
const actionHistory = await getActionHistoryAsync(); // need to wait async thunk (all inner dispatch)
expect(actionHistory).toEqual([
{type: 'TOGGLE_LOADING', payload: true},
{type: 'SOME_BACKEND_REQUEST', payload: 'success response'},
{type: 'TOGGLE_LOADING', payload: false},
]);
expect(store.getState().exampleSimple).toEqual({
loading: false,
result: 'success response'
});
console.log(await getActionHistoryStringifyAsync({withColor: true}));
});
});
I recommend you to write something like this to avoid async problems:
return store
.dispatch(actionCreators.login({}))
.then(() => expect(store.getActions()).toEqual(expectedActions));
Related
I'm trying to mock a call to AWS.DynamoDB.DocumentClient. I tried several solutions I found online, but I cannot get it to work.
This is my best effort so far:
import * as AWS from 'aws-sdk';
import * as dynamoDbUtils from '../../src/utils/dynamo-db.utils';
jest.mock("aws-sdk");
describe('dynamo-db.utils', () => {
describe('updateEntity', () => {
it('Should return', async () => {
AWS.DynamoDB.DocumentClient.prototype.update.mockImplementation((_, cb) => {
cb(null, user);
});
await dynamoDbUtils.updateEntity('tableName', 'id', 2000);
});
});
});
I get error message
Property 'mockImplementation' does not exist on type '(params: UpdateItemInput, callback?: (err: AWSError, data: UpdateItemOutput) => void) => Request<UpdateItemOutput, AWSError>'.ts(2339)
My source file:
import AWS from 'aws-sdk';
let db: AWS.DynamoDB.DocumentClient;
export function init() {
db = new AWS.DynamoDB.DocumentClient({
region: ('region')
});
}
export async function updateEntity(tableName: string, id: string, totalNumberOfCharacters: number): Promise<AWS.DynamoDB.UpdateItemOutput> {
try {
const params = {
TableName: tableName,
Key: { 'id': id },
UpdateExpression: 'set totalNumberOfCharacters = :totalNumberOfCharacters',
ExpressionAttributeValues: {
':totalNumberOfCharacters': totalNumberOfCharacters
},
ReturnValues: 'UPDATED_NEW'
};
const updatedItem = await db.update(params).promise();
return updatedItem;
} catch (err) {
throw err;
}
}
Please advise how can I properly mock the response of AWS.DynamoDB.DocumentClient.update
Have some way to do the that thing (I think so).
This is one of them:
You use AWS.DynamoDB.DocumentClient, then we will mock AWS object to return an object with DocumentClient is mocked object.
jest.mock("aws-sdk", () => {
return {
DynamoDB: {
DocumentClient: jest.fn(),
},
};
});
Now, AWS.DynamoDB.DocumentClient is mocked obj. Usage of update function like update(params).promise() => Call with params, returns an "object" with promise is a function, promise() returns a Promise. Do step by step.
updateMocked = jest.fn();
updatePromiseMocked = jest.fn();
updateMocked.mockReturnValue({
promise: updatePromiseMocked,
});
mocked(AWS.DynamoDB.DocumentClient).mockImplementation(() => {
return { update: updateMocked } as unknown as AWS.DynamoDB.DocumentClient;
});
mocked import from ts-jest/utils, updateMocked to check the update will be call or not, updatePromiseMocked to control result of update function (success/ throw error).
Complete example:
import * as AWS from 'aws-sdk';
import * as dynamoDbUtils from './index';
import { mocked } from 'ts-jest/utils'
jest.mock("aws-sdk", () => {
return {
DynamoDB: {
DocumentClient: jest.fn(),
},
};
});
describe('dynamo-db.utils', () => {
describe('updateEntity', () => {
let updateMocked: jest.Mock;
let updatePromiseMocked: jest.Mock;
beforeEach(() => {
updateMocked = jest.fn();
updatePromiseMocked = jest.fn();
updateMocked.mockReturnValue({
promise: updatePromiseMocked,
});
mocked(AWS.DynamoDB.DocumentClient).mockImplementation(() => {
return { update: updateMocked } as unknown as AWS.DynamoDB.DocumentClient;
});
dynamoDbUtils.init();
});
it('Should request to Dynamodb with correct param and forward result from Dynamodb', async () => {
const totalNumberOfCharacters = 2000;
const id = 'id';
const tableName = 'tableName';
const updatedItem = {};
const params = {
TableName: tableName,
Key: { 'id': id },
UpdateExpression: 'set totalNumberOfCharacters = :totalNumberOfCharacters',
ExpressionAttributeValues: {
':totalNumberOfCharacters': totalNumberOfCharacters
},
ReturnValues: 'UPDATED_NEW'
};
updatePromiseMocked.mockResolvedValue(updatedItem);
const result = await dynamoDbUtils.updateEntity(tableName, id, totalNumberOfCharacters);
expect(result).toEqual(updatedItem);
expect(updateMocked).toHaveBeenCalledWith(params);
});
});
});
i have this bootstrap vue component:
<b-form-input
v-model="currentUser.name"
placeholder="Name *"
name="name"
#input="checkSubmitStatus()"
></b-form-input>
checkSubmitStatus in the methods goes to call updateSubmitDisabled which I have in the mutations inside another file:
methods: {
...mapMutations({
updateSubmitDisabled: "updateSubmitDisabled"
}),
checkSubmitStatus() {
const isDisabled = this.currentUser.name.length === 0;
this.updateSubmitDisabled(isDisabled);
}
}
this is the .spec.js file:
import { createLocalVue, mount } from "#vue/test-utils";
import Vue from "vue";
import Vuex from 'vuex';
import UserForm from "#/components/event-created/UserForm.vue";
import { BootstrapVue, BootstrapVueIcons } from "bootstrap-vue";
const localVue = createLocalVue();
localVue.use(BootstrapVue);
localVue.use(BootstrapVueIcons);
localVue.use(Vuex);
describe("UserForm.vue", () => {
let mutations;
let store;
beforeEach(() => {
mutations = {
updateSubmitDisabled: jest.fn()
};
store = new Vuex.Store({
state: {
currentUser: {
name: 'pippo',
}
},
mutations
});
})
it("should call the updateSubmitDisabled mutation", async () => {
const wrapper = mount(UserForm, { localVue, store });
const input = wrapper.get('input[name="name"]');
await Vue.nextTick();
input.element.value = 'Test';
await input.trigger('input');
await Vue.nextTick();
expect(mutations.updateSubmitDisabled).toHaveBeenCalled();
});
});
for now I just want to test if "updateSubmitDisabled" is called on "name" but as a result the test says:
Expected number of calls:> = 1
Received number of calls: 0
I finally settled with:
it("should call the updateSubmitDisabled mutation", () => {
const wrapper = mount(UserForm, { localVue, store });
const input = wrapper.get('input[name="name"]');
input.element.dispatchEvent(new Event('input'));
expect(mutations.updateSubmitDisabled).toHaveBeenCalled();
});
I'm having troubles stubbing out an async action for redux-thunk. Mocha keeps giving me timeout:
// actions.js
const fetchUserThings = userId => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([{ foo: 'bar' }]);
}
});
};
const displayUserThings = userThings => ({
type: 'DISPLAY',
userThings
});
export const loadUserThings = userId => {
return dispatch => {
fetchUserThings(userId)
.then(userThings => displayUserThings(userThings));
};
}
And here's my mocha test. I'm using redux on version 3.3.1 and redux-mock-store on version 0.0.6.
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import * as actions from './actions';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('actions', () => {
it('creates DISPLAY after fetching', done => {
const userId = 1;
const userThings = [{ foo: 'bar' }];
const expectedActions = [
{ type: 'DISPLAY', userThings }
];
const store = mockStore({userThings: []}, expectedActions, done);
store.dispatch(actions.loadUserThings(userId);
});
});
What am I missing?
In my React Redux app, I tried to do some tests to one of my function that has settimeout in it
// MyFunc.js
export function updateSomething(id, data) {
return (dispatch, getState) => {
dispatch({type: 'dispatch1'})
settimeout(() => {
axios.get('/data')
.then((res) => {
dispatch({type: 'timeoutted_dispatch', data: res.data})
})
.catch(error => {
dispatch({type: 'dispatch_error'});
})
},3000);
dispatch({type: 'dispatch_end'});
}
}
// MyTest.js
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';;
import MockAdapter from 'axios-mock-adapter';
import axios from 'axios';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('Lets do some tests', () => {
it('Should dispatch timeoutted function', () => {
const store = mockStore({});
// Get all the dispatches for testing purpose
store.dispatch(updateSomething(1, {test: 'test'}))
.then(() => {
const actionList = store.getAction();
expect(actionList).toEqual(allDispatches);
// At this point, I won't be able to get anything inside timeout function
})
});
})
How do I set some kind of Await so I can wait for the timeout before trying to retrieve the data ?
I'm currently trying to test my thunk action (getUserFeatureNames) to see if it calls a success action(getUserFeatureNamesSuccess) using jest. getUserFeatureNames thunk action currently resides in loginActions.js file which is import homeQueries(which i'm trying to mock). So far I'm getting the following error when running my jest test..
TypeError: _homeQueries2.default.getFeatureNames is not a function
How do i mock homeQueries.getFeatureNames?
function createStore(state = {}, expectActions = {}){
const mockStore = configureMockStore([thunk]);
return mockStore(state, expectActions);
}
describe("home_async_tests", () => {
test("getUserFeatureNamesSuccess action is called if request was success", (done) => {
jest.mock('../../../graphQL/homeQueries', () => {
return jest.fn(() => {
{
getFeatureNames: () =>{
return new Promise((resolve, reject) => {
let array = [{iconFile: 'Personalization.png', description: 'Personalization'},{iconFile: 'Home.png', description: 'Home'}];
resolve(array);
});
};
}
});
});
jest.dontMock('../../../app/redux/actions/homeActions');
let homeActions = require('../../../app/redux/actions/homeActions');
const expectedAction = {type: types.userFeatureNamesSuccess, payLoad: {isError: false, data: '' }};
const store = createStore();
store.dispatch(homeActions.getUserFeatureNames({token:"fdis4554" })).then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(expectedAction.type);
expect(actions[0].payLoad.isError).toEqual(expectedAction.payLoad.isError);
done();
});
});
I assume that the module just return an object and not a function that returns an object, so your mock should look like this:
jest.mock('../../../graphQL/homeQueries', () = > ({
getFeatureNames: () = > {
return new Promise((resolve, reject) = > {
let array = [{
iconFile: 'Personalization.png',
description: 'Personalization'
}, {
iconFile: 'Home.png',
description: 'Home'
}];
resolve(array);
});
};
}
});