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!