Unit testing reducer functions with Jest

Unit testing reducer functions with Jest

·

3 min read

We mostly check if our app is working as it is intended to in the UI. If the UI breaks, we would know that there is something wrong with our logic. But what if we could do this without breaking the UI? Yes, this is where we could use testing. It tells us beforehand if our logic is correct. Supercool isn't it?

Unit testing as the name suggests tests a small piece of code .You write unit tests only for the logic that you have written. If you have to test other logics as well then you would have to go with end to end testing

In this blog, we will see how to write unit tests for reducer functions. We will also see some of the best practices for writing tests.

The reducer functions contain the main logic of our application and hence it is very important to test them.

Let's get started

Let's create a reducer function for cart with three action types

cartReducer.js

export const cartReducer = (state, action) => {
  switch (action.type) {
    case "ADD_TO_CART":
      return {
        cart: state.cart.concat(action.payload.item)
      };
    case "REMOVE_FROM_CART":
      return {
        cart: state.cart.filter((item) => item.id !== action.payload.id)
      };
    case "INCREMENT_CART_QUANTITY":
      return {
        cart: state.cart.map((item) =>
          item.id === action.payload.id
            ? {
                ...item,
                quantity: item.quantity + 1
              }
            : item
        )
      };
    case "DECREMENT_CART_QUANTITY":
      return {
        cart: state.cart.map((item) =>
          item.id === action.payload.id
            ? {
                ...item,
                quantity: item.quantity - 1
              }
            : item
        )
      };
    default:
      throw new Error("Unhandled action");
  }
};

We need to create a test file for cart reducer function to write tests for it. It should have the extension .test.js. In our case, it would be cartReducer.test.js

Jest provides methods such as describe, test to write unit tests. Let us see what they are

  • describe(name, callback): It is used to group the tests together and also provide a descriptive name to the grouped tests.
  • test(name, callback): Inside the test function we would be actually testing our logic. We will see that soon
describe("testing cart reducer", () => {
    test("adds item to cart", () => {
  });
    test("removes item from cart", () => {
 });
});

Let us write our first test for adding an item to the cart. Before we do that we need to import the cartReducer function in our test file.

Our cartReducer function takes two parameters state, action and it would return a new state

const state = cartReducer(initialState, action);

We need to define an initial state to pass to the reducer function. Initially let us consider the cart to be empty. We should also define the action.The action takes two parameters types and payload

const initialState = {
      cart: []
    };

 const action = {
      type: "ADD_TO_CART",
      payload: {
        item: {
          id: "234",
          name: "Shoes",
          unitPrice: 123,
          quantity: 1
        }
      }
    };

The final state after adding an item to the cart should be const finalState={ cart: [ { id: "234", name: "Shoes", unitPrice: 123, quantity: 1 } ] } In order to check if the final state is equivalent to the state that is returned from the reducer, Jest provides a function **toEqual**

 expect(state).toEqual(finalState);

We could similarly test other actions as well. But what if there is an unhandled action. How do we test that? In our reducer function, we throw an error for unhandled action. It could be tested as follows

test("unhandled action", () => {
    const initialState = {
      cart: [
        {
          id: "234",
          name: "Shoes",
          unitPrice: 123,
          quantity: 2
        }
      ]
    };
    const action = {
      type: "RESET",
      payload: {
        cart: []
      }
    };
    expect(() => cartReducer(initialState, action)).toThrow();
  });

The complete code could be found here https://codesandbox.io/s/cart-reducer-unit-testing-l9cxz

Some Best practices for testing:

  • Each test should be independent, it should not depend on the previous test
  • Each test should test only one thing
  • It should be fast and automatic

Happy learning! Hope you learned something new from this blog. I would love to connect with you on Twitter Linkedin