Unit testable components pt.4: Testing the Redux data flow

Testing actions within actions

Testing redux is verbose. Sometimes it is even difficult. But it doesn't matter where you do it. It will always be challenging. The difference is that the DOM is irrelevant for my approach to testing.

I use redux-mock-store to test my actions. No need for enzyme. One less moving part to deal with. That is the whole point of this testing style. Here is what a sample test would look like

The action process

            
const processHomeMount = () =>
    async (dispatch, getState) => {
        try {
            const type = getState().home.topSearches.focusedDateRange
            const { endDate, startDate } = createPresentDateRange({ daysAgo: 100 })
            await Promise.all([
                dispatch(fetchPopularSearches({ type, limit: TOP_SEARCH_ENTRY_COUNT })),
                dispatch(fetchNews({ startDate, endDate }))
            ])
        } catch (error) {
            return new Error(error)
        }
    }
            
        

Imports

            
// middleware to be passed into mocked store
import thunk from 'redux-thunk'
// action testing npm package
import configureStore from 'redux-mock-store'

import {
    getPopularSearches,
    getNews,
} from "Api/ApiCalls/HomeApi/HomeApi"
            
        

Mocks

            
// mock the functions so I can use returnValueOnce
jest.mock('Api/ApiCalls/HomeApi/HomeApi')

// api call return value mock
getPopularSearches.returnValueOnce(popularSearchesPayload)
getNews.returnValueOnce(newsPayload)

// setting up my mock store that dispatches actions and keeps track of emitted actions
const initialState = {}
const middleware = [thunk]
const mockStore = configureStore(middleware)(initialState)
            
        

Assertion

            
const processHomeMountExpectedActions = [
    {
        news: [{
            content: `Welcome to my app!`,
            createdAt: '2018-12-06T07:47:51.119Z',
            id: 2,
            title: 'sample news entry!',
            version: null
        }],
        type: 'SET_NEWS'
    },
    {
        type: 'SET_FOCUSED_TOP_SEARCH',
        focusedDateRange: 'week',
    },
    {
        type: 'SET_TOP_SEARCH_ENTRIES',
        entries: [{name: 'the office'}]
    },
]

describe('processHomeMount', () => {
    beforeEach(() => {
        // remove all actions from mockStore before each assertion in test file
        mockStore.clearActions() 
    })
    it('dispatches expected objects', () => {
        mockStore.dispatch(processHomeMount())
        const emittedActions = mockStore.getActions() // array of all actions run
        expect(emittedActions).toEqual(processHomeMountExpectedActions)
    })
})
            
        

All done

There you have it. Test Redux and React components in their own respective environments. Use enzyme to test components and redux-mock-store for your business logic. Not both at once.

Comments

Popular posts from this blog

Contrasting var and ES6s new declarations: let and const

Modularity vs. Abstraction. Which is More Important?

Unit testable components pt.3: Testing components