Reputation: 979
What is the best way to test this function
export function receivingItems() {
return (dispatch, getState) => {
axios.get('/api/items')
.then(function(response) {
dispatch(receivedItems(response.data));
});
};
}
this is currently what I have
describe('Items Action Creator', () => {
it('should create a receiving items function', () => {
expect(receivingItems()).to.be.a.function;
});
});
Upvotes: 6
Views: 6450
Reputation: 904
I solved this in a different way: injecting axios as a dependency of action. I prefer this approach over 'rewiring' dependencies.
So I used the same approach of testing redux-connected components. When I export actions I export two versions: one with (to be used for components) and one without (for testing) binding dependencies.
Here is how my actions.js file looks like:
import axios from 'axios'
export const loadDataRequest = () => {
return {
type: 'LOAD_DATA_REQUEST'
}
}
export const loadDataError = () => {
return {
type: 'LOAD_DATA_ERROR'
}
}
export const loadDataSuccess = (data) =>{
return {
type: 'LOAD_DATA_SUCCESS',
data
}
}
export const loadData = (axios) => {
return dispatch => {
dispatch(loadDataRequest())
axios
.get('http://httpbin.org/ip')
.then(({data})=> dispatch(loadDataSuccess(data)))
.catch(()=> dispatch(loadDataError()))
}
}
export default {
loadData: loadData.bind(null, axios)
}
Then testing with jest (actions.test.js):
import { loadData } from './actions'
describe('testing loadData', ()=>{
test('loadData with success', (done)=>{
const get = jest.fn()
const data = {
mydata: { test: 1 }
}
get.mockReturnValue(Promise.resolve({data}))
let callNumber = 0
const dispatch = jest.fn(params =>{
if (callNumber===0){
expect(params).toEqual({ type: 'LOAD_DATA_REQUEST' })
}
if (callNumber===1){
expect(params).toEqual({
type: 'LOAD_DATA_SUCCESS',
data: data
})
done()
}
callNumber++
})
const axiosMock = {
get
}
loadData(axiosMock)(dispatch)
})
})
When using the actions inside a component I import everything:
import Actions from './actions'
And to dispatch:
Actions.loadData() // this is the version with axios binded.
Upvotes: 2
Reputation: 268215
From Redux “Writing Tests” recipe:
For async action creators using Redux Thunk or other middleware, it’s best to completely mock the Redux store for tests. You can still use
applyMiddleware()
with a mock store, as shown below (you can find the following code in redux-mock-store). You can also use nock to mock the HTTP requests.function fetchTodosRequest() { return { type: FETCH_TODOS_REQUEST } } function fetchTodosSuccess(body) { return { type: FETCH_TODOS_SUCCESS, body } } function fetchTodosFailure(ex) { return { type: FETCH_TODOS_FAILURE, ex } } export function fetchTodos() { return dispatch => { dispatch(fetchTodosRequest()) return fetch('http://example.com/todos') .then(res => res.json()) .then(json => dispatch(fetchTodosSuccess(json.body))) .catch(ex => dispatch(fetchTodosFailure(ex))) } }
can be tested like:
import expect from 'expect' import { applyMiddleware } from 'redux' import thunk from 'redux-thunk' import * as actions from '../../actions/counter' import * as types from '../../constants/ActionTypes' import nock from 'nock' const middlewares = [ thunk ] /** * Creates a mock of Redux store with middleware. */ function mockStore(getState, expectedActions, done) { if (!Array.isArray(expectedActions)) { throw new Error('expectedActions should be an array of expected actions.') } if (typeof done !== 'undefined' && typeof done !== 'function') { throw new Error('done should either be undefined or function.') } function mockStoreWithoutMiddleware() { return { getState() { return typeof getState === 'function' ? getState() : getState }, dispatch(action) { const expectedAction = expectedActions.shift() try { expect(action).toEqual(expectedAction) if (done && !expectedActions.length) { done() } return action } catch (e) { done(e) } } } } const mockStoreWithMiddleware = applyMiddleware( ...middlewares )(mockStoreWithoutMiddleware) return mockStoreWithMiddleware() } describe('async actions', () => { afterEach(() => { nock.cleanAll() }) it('creates FETCH_TODOS_SUCCESS when fetching todos has been done', (done) => { nock('http://example.com/') .get('/todos') .reply(200, { todos: ['do something'] }) const expectedActions = [ { type: types.FETCH_TODOS_REQUEST }, { type: types.FETCH_TODOS_SUCCESS, body: { todos: ['do something'] } } ] const store = mockStore({ todos: [] }, expectedActions, done) store.dispatch(actions.fetchTodos()) }) })
Upvotes: 12
Reputation: 22379
I would use a stub axios
(for example by using mock-require) and write a test that actually calls receivingItems()(dispatch, getState)
and makes sure dispatch
is called with the correct data.
Upvotes: 2