Testing Redux

Reducers

Testing reducers should be straight forward. Same data in, same data out. Another thing to consider is to use action creators when creating the action for the reducer under test. By taking this approach we don’t need to explicitly test any action creators, as this might involve some overhead without real benefits.

it('should handle ADD_TODO', () => {

expect(

todos([], addTodo('Test AddTodo') // use the action creator

).toEqual([

{

text: 'Test AddTodo',

completed: false,

id: 0

}

])

})

Bonus: Using generative tests to verify reducers. Read this for or a more detailed writeup.

Action Creator

See reducers. No explicit action creator tests. See the following tests, we’re rewriting the action creator to be able to test it.

it('addTodo should create ADD_TODO action', () => {

expect(addTodo('Test addTodo')).toEqual({

type: types.ADD_TODO,

text: 'Test addTodo'

})

})

Even a better example.

const completeAll = () => ({ type: types.COMPLETE_ALL }) expect(completeAll()).toEqual({

type: types.COMPLETE_ALL

})

By creating the actions via action creators when testing reducers, we’re already verifying that the action creators work as expected.

Async Action Creators with Redux-Thunk

Testing includes mocking the store or mocking a specific call. There are a couple of possible approaches, but best is to consult the redux documentation.

The following example is taken straight from redux “Writing Tests” section.

it('creates FETCH_TODOS_SUCCESS when fetching todos has been done', () => {

nock('http://example.com/')

.get('/todos')

.reply(200, { body: { todos: ['do something'] }})



const expectedActions = [

{ type: types.FETCH_TODOS_REQUEST },

{ type: types.FETCH_TODOS_SUCCESS,

body: { todos: ['do something'] }

}]

const store = mockStore({ todos: [] })



return store.dispatch(actions.fetchTodos())

.then(() => { // return of async actions

expect(store.getActions()).toEqual(expectedActions)

})

})

Yes, asynchronous action creators based on redux-thunk for example should be tested when possible.

Async Action Creators with Redux-Saga

Testing asynchronous actions with redux-saga is as simple as calling next on the generated object. Take a look at the shopping-cart test example:

test('getProducts Saga test', function (t) {

const generator = getAllProducts(getState)

let next = generator.next(actions.getAllProducts())



t.deepEqual(next.value, call(api.getProducts),

"must yield api.getProducts"

)



next = generator.next(products)



t.deepEqual(next.value, put(actions.receiveProducts(products)),

"must yield actions.receiveProducts(products)"

)

t.end()

})

We don’t have to deal with mocking api calls and other asynchronous actions, as we only verify if the returned action is the expected one. This also enables us to verify that the actions are returned in the correct order.

Testing React-Redux

No testing of connected components nor verifying that mapDispatchToProps returns expected results at the moment. Regarding the first, I would like to hear some feedback. It could also be seen as a type of integration test, but I would like to hear about the real benefits, besides testing if react-redux is passing the updated state to a given component under test. Regarding testing mapDispatchToProps, under regular circumstances, we’re only composing action creators with dispatch.

In regards to the connect method, mapStateToProps might be interesting to test, especially when defined as a selector. This depends on the fact if logic is involved when selecting the state and might make a lot of sense in given situations.

Like mentioned above, I would like to hear feedback on how you approach testing the react-redux specific parts.