Let's test our Redux containers:
- Redux containers should not have any JSX code; the best practice is to have mapStateToProps and mapDispatchToProps in our connect method passing another component (such as a Layout component) in the export, for example, let's see our Todo List Container:
// Dependencies
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
// Components
import Layout from '../components/Layout';
// Actions
import { fetchTodo } from '../actions';
export default connect(({ todo }) => ({
todo: todo.list
}), dispatch => bindActionCreators(
{
fetchTodo
},
dispatch
))(Layout);
File: src/client/todo/container/index.js
- You might be wondering what exactly we need to test in here. Well, the most important things we need to test in a container are the action dispatch (the fetchTodo action) and get our todo state from Redux with data. That being said, this is our container unit test file:
// Dependencies
import React from 'react';
import { shallow } from 'enzyme';
import configureStore from 'redux-mock-store';
// Actions
import { fetchTodo } from '../actions';
// Testable Container
import Container from './index';
// Mocking Initial State
const mockInitialState = {
todo: {
list: [
{
id: 1,
title: 'Go to the Gym'
},
{
id: 2,
title: 'Dentist Appointment'
},
{
id: 3,
title: 'Finish homework'
}
]
}
};
// Configuring Mock Store
const mockStore = configureStore()(mockInitialState);
// Mocking the Actions
jest.mock('../actions', () => ({
fetchTodo: jest.fn().mockReturnValue({ type: 'mock-FETCH_TODO_SUCCESS' })
}));
describe('Todo Container', () => {
let mockParams;
let container;
beforeEach(() => {
fetchTodo.mockClear();
mockParams = {};
mockStore.clearActions();
container = shallow(<Container {...mockParams} store={mockStore} />);
});
it('should dispatch fetchTodo', () => {
const { fetchTodo } = container.props();
fetchTodo();
const actions = mockStore.getActions();
expect(actions).toEqual([{ type: 'mock-FETCH_TODO_SUCCESS' }]);
});
it('should map todo and get the todo list from Initial State', () => {
const { todo } = container.props();
const { todo: { list }} = mockInitialState;
expect(todo).toEqual(list);
});
});
File: src/client/todo/container/index.test.js
- Test the fetchTodo action. This is the code for our action file:
// Base Actions
import { request, received } from '@baseActions';
// Api
import api from '../api';
// Action Types
import { FETCH_TODO } from './actionTypes';
export const fetchTodo = () => dispatch => {
const action = FETCH_TODO;
const { fetchTodo } = api;
dispatch(request(action));
return fetchTodo()
.then(response => dispatch(received(action, response.data)));
};
File: src/client/todo/actions/index.js
- This is our actionTypes.js file:
// Actions
export const FETCH_TODO = {
request: () => 'FETCH_TODO_REQUEST',
success: () => 'FETCH_TODO_SUCCESS'
};
File: src/client/todo/actions/actionTypes.js
- To test an async Redux Action, we need to use redux-thunk and moxios to test an action that is using axios to retrieve data from the server. Our test file should look like this:
// Dependencies
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import moxios from 'moxios';
// Action
import { fetchTodo } from './index';
// Action Types
import { FETCH_TODO } from './actionTypes';
// Configuring Store with Thunk middleware
const mockStore = configureMockStore([thunk]);
// Response Mock
const todoResponseMock = [
{
id: 1,
title: 'Go to the Gym'
},
{
id: 2,
title: 'Dentist Appointment'
},
{
id: 3,
title: 'Finish homework'
}
];
describe('fetchTodo action', () => {
beforeEach(() => {
moxios.install();
});
afterEach(() => {
moxios.uninstall();
});
it('should fetch the Todo List', () => {
moxios.wait(() => {
const req = moxios.requests.mostRecent();
req.respondWith({
status: 200,
response: todoResponseMock
});
});
const expectedActions = [
{
type: FETCH_TODO.request()
},
{
type: FETCH_TODO.success(),
payload: todoResponseMock
}
];
const store = mockStore({ todo: [] })
return store.dispatch(fetchTodo()).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
});
File: src/client/todo/actions/index.test.js
- Let's test our reducer. This is the Todo reducer file:
// Utils
import { getNewState } from '@utils/frontend';
// Action Types
import { FETCH_TODO } from '../actions/actionTypes';
// Initial State
const initialState = {
list: []
};
export default function todoReducer(state = initialState, action) {
switch (action.type) {
case FETCH_TODO.success(): {
const { payload: { response = [] } } = action;
return getNewState(state, {
list: response
});
}
default:
return state;
}
}
File: src/client/todo/reducer/index.js
- We need to test two things in our reducer: the initial state and the state when the FETCH_TODO action is a success:
// Reducer
import todo from './index';
// Action Types
import { FETCH_TODO } from '../actions/actionTypes';
// Initial State
const initialState = {
list: []
};
describe('Todo List Reducer', () => {
it('should return the initial state', () => {
const expectedInitialState = todo(undefined, {});
expect(expectedInitialState).toEqual(initialState);
});
it('should handle FETCH_TODO when is success', () => {
const action = {
type: FETCH_TODO.success(),
payload: {
response: [
{
id: 1,
title: 'Go to the Gym'
},
{
id: 2,
title: 'Dentist Appointment'
},
{
id: 3,
title: 'Finish homework'
}
]
}
};
const expectedState = {
list: action.payload.response
};
const state = todo(initialState, action);
expect(state).toEqual(expectedState);
});
});
File: src/client/todo/reducer/index.test.js