Mastering Jest: Day 6 — Testing Redux with Jest
Introduction
Photo by Chris Ried on Unsplash
Welcome to Day 6 of our Jest journey! Yesterday, we explored the power of snapshot testing. Today, we’ll focus on testing Redux, a popular state management library, with Jest. Testing Redux ensures that your state management logic is robust and behaves as expected.
Setting Up Redux for Testing
To get started, let’s create a simple Redux setup:
// actions.js
export const increment = () => ({ type: 'INCREMENT' });
export const decrement = () => ({ type: 'DECREMENT' });
// reducer.js
const initialState = { count: 0 };
export default function counterReducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
}
Testing Actions
Actions are simple objects that describe changes. Here’s how to test them:
// actions.test.js
import { increment, decrement } from './actions';
test('increment action', () => {
expect(increment()).toEqual({ type: 'INCREMENT' });
});
test('decrement action', () => {
expect(decrement()).toEqual({ type: 'DECREMENT' });
});
Testing Reducers
Reducers specify how the state changes in response to actions. Here’s how to test a reducer:
// reducer.test.js
import counterReducer from './reducer';
test('should return the initial state', () => {
expect(counterReducer(undefined, {})).toEqual({ count: 0 });
});
test('should handle INCREMENT', () => {
expect(counterReducer({ count: 0 }, { type: 'INCREMENT' })).toEqual({ count: 1 });
});
test('should handle DECREMENT', () => {
expect(counterReducer({ count: 1 }, { type: 'DECREMENT' })).toEqual({ count: 0 });
});
Testing Redux with Connected Components
To test a component connected to a Redux store, we use react-redux
's Provider
to wrap the component with a mock store.
// Counter.js
import React from 'react';
import { connect } from 'react-redux';
import { increment, decrement } from './actions';
function Counter({ count, increment, decrement }) {
return (
{count}
+
-
);
}
const mapStateToProps = (state) => ({ count: state.count });
export default connect(mapStateToProps, { increment, decrement })(Counter);
Here’s how to test the Counter
component:
// Counter.test.js
import React from 'react';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import counterReducer from './reducer';
import Counter from './Counter';
function renderWithRedux(component, { initialState, store = createStore(counterReducer, initialState) } = {}) {
return {
...render({component}),
store,
};
}
test('renders with initial state from Redux store', () => {
const { getByText } = renderWithRedux();
expect(getByText('0')).toBeInTheDocument();
});
test('can increment the count', () => {
const { getByText } = renderWithRedux();
fireEvent.click(getByText('+'));
expect(getByText('1')).toBeInTheDocument();
});
test('can decrement the count', () => {
const { getByText } = renderWithRedux(, { initialState: { count: 1 } });
fireEvent.click(getByText('-'));
expect(getByText('0')).toBeInTheDocument();
});
Conclusion
Testing Redux with Jest ensures that your state management logic is sound and your application behaves as expected. By isolating actions, reducers, and connected components, you can confidently validate the integrity of your state management.
Stay tuned for Day 7, where we’ll wrap up our Jest series with advanced testing techniques. Feel free to share your experiences and any questions you have in the comments below!
Engagement
How do you approach testing Redux in your projects? Any tips or challenges you’ve encountered? Let’s discuss!