Running a Test with Multiple Test Cases using Jest/Enzyme (React)

Running a Test with Multiple Test Cases using Jest/Enzyme (React)

React Aug 10, 2019

Last year I wrote an article which outlines some tips I had for running a test with multiple test cases using Jasmine: /running-multiple-test-cases-in-jasmine/

Both at work and on my personal side projects, I've started to use React a lot more than Angular. Since Create React App comes with Jest/Enzyme out of the box, I thought I would write another article outlining how I run multiple test cases in Jest/Enzyme for my React projects!

Simple Tests Cases

If we had the following simple class that we wanted to test:

export default class CalculatorService {
  average = numberList =>
    numberList.reduce((acc, val) => acc + val, 0) / numberList.length;
}

We might have created a test for our CalculatorService like:

import CalculatorService from "./calculator.service";

describe("CalculatorService", () => {
  let calculatorService;

  beforeEach(() => {
    calculatorService = new CalculatorService();
  });

  describe("average", () => {
    it("should average the number list correctly", () => {
      const numberList = [1, 4, 5, 10];
      const res = calculatorService.average(numberList);

      expect(res).toEqual(5);
    });
  });
});

But now we want to easy run multiple test cases without creating a new statement each time. The best way that I've found to structure my tests this way is by running a forEach loop over an it statement. It's important that we have the forEach outside of the it statement otherwise we won't be able to provide information to the it statement that will inform us which test case in particular has failed. This is how I would write test cases for CalculatorService:

  describe("average", () => {
    const testCases = [
      {
        numberList: [1, 2, 3],
        expected: 2
      },
      {
        numberList: [1, 4, 10],
        expected: 5
      }
    ];

    testCases.forEach(test => {
      it(`should correctly find the average of: ${test.numberList} which is: ${
        test.expected
      }`, () => {
        const res = calculatorService.average(test.numberList);

        expect(res).toEqual(test.expected);
      });
    });
  });

As you can see, having the it statement outside allows us to write descriptive test cases which will provide an output of:

jest-testing

This allows us to pin-point exactly which test case has failed and rectify it.

React Components

Applying this forEach loop methodology to testing React components using shallow rendering from the Enzyme library works very well! For this example, I'll be testing the following React component:

import React from "react";
import "./List.css";

const List = ({ items, itemClickEvent }) => (
  <div className="list-container">
    {items.map(item => (
      <div
        key={item.id}
        onClick={() => itemClickEvent(item.id)}
        className="item-container"
      >
        <h2 className="heading">{item.heading}</h2>
        <div className="description">{item.description}</div>
      </div>
    ))}
  </div>
);

export default List;

I was able to create 3 test cases using the forEach structure:

import React from "react";
import User from "./User";
import List from "./List";
import { shallow } from "enzyme";
import { configure } from "enzyme";
import Adapter from "enzyme-adapter-react-16";

configure({ adapter: new Adapter() });

describe("List", () => {
  const testItems = [
    {
      id: 45,
      heading: "Test Item",
      description: "This is a test item"
    },
    {
      id: 75,
      heading: "Test Item 2",
      description: "This is a test item"
    },
    {
      id: 90,
      heading: "Test Item 3",
      description: "This is a test item"
    },
    {
      id: 150,
      heading: "Test Item 4",
      description: "This is a test item"
    },
    {
      id: 270,
      heading: "Test Item 5",
      description: "This is a test item"
    }
  ];

  let testCases = [
    {
      itemAt: 0,
      expectedToCallWith: 45
    },
    {
      itemAt: 2,
      expectedToCallWith: 90
    },
    {
      itemAt: 4,
      expectedToCallWith: 265
    }
  ];

  testCases.forEach(test => {
    it(`should call correctly call itemClickEvent with ${
      test.expectedToCallWith
    } when item at: ${test.itemAt} is clicked`, () => {
      const mockItemClickEventHandler = jest.fn();
      const props = {
        items: testItems,
        itemClickEvent: mockItemClickEventHandler
      };

      const listComponent = shallow(<List {...props} />);
      listComponent
        .find(".list-container")
        .children()
        .at(test.itemAt)
        .simulate("click");

      expect(mockItemClickEventHandler).toHaveBeenCalledWith(
        test.expectedToCallWith
      );
    });
  });
});

A failure of a test would give a helpful error message of:
jest-testing-2-1

React Component Snapshots

Again, this can be applied to React Snapshots. If we had the component:

import React from "react";
import "./user.css";

const User = ({ imgUrl, firstName, lastName, age, address }) => {
  return (
    <div className="user-container">
      <img src={imgUrl} />
      <div className="information" />
      <h1>{`${firstName} ${lastName}`}</h1>
      <span>{age}</span>
      <span>{address}</span>
    </div>
  );
};

export default User;

We can easily create multiple Snapshot test cases using react-test-renderer like so:

import React from "react";
import renderer from "react-test-renderer";
import User from "./User";

describe("User", () => {
  describe("Snapshots", () => {
    let testCases = [
      {
        imgUrl: "https://test.com",
        firstName: "Harvey",
        lastName: "Delaney",
        age: "69",
        address: "123 Fake Street"
      },
      {
        imgUrl: "https://test2.com",
        firstName: "John",
        lastName: "Doe",
        age: "35",
        address: "123 Real Street"
      }
    ];

    testCases.forEach(test => {
      it(`should have the correct snapshot for ${test.firstName} ${
        test.lastName
      }`, () => {
        const userComponent = renderer.create(<User {...test} />).toJSON();
        expect(userComponent).toMatchSnapshot();
      });
    });
  });
});

This will output two Snapshots:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`User Snapshots should have the correct snapshot for Harvey Delaney 1`] = `
<div
  className="user-container"
>
  <img
    src="https://test.com"
  />
  <div
    className="information"
  />
  <h1>
    Harvey Delaney
  </h1>
  <span>
    69
  </span>
  <span>
    123 Fake Street
  </span>
</div>
`;

exports[`User Snapshots should have the correct snapshot for John Doe 1`] = `
<div
  className="user-container"
>
  <img
    src="https://test2.com"
  />
  <div
    className="information"
  />
  <h1>
    John Doe
  </h1>
  <span>
    35
  </span>
  <span>
    123 Real Street
  </span>
</div>
`;

I hope this article has helped give you a bit of insight into how duplicated tests can be written more concisely by opting to use a simple forEach loop!

Tags

Harvey Delaney

Front End Engineer II at Amazon Web Services

Exclusive Usenet provider deals

Harvey's essential software engineering books

1

The Pragmatic Programmer: From Journeyman to Master

2

Clean Code: A Handbook of Agile Software Craftsmanship

3

Code Complete: A Practical Handbook of Software Construction

4

Design Patterns: Elements of Reusable Object-Oriented Software

Harvey is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to amazon.com
Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.