How to Test React Components with Storybook Interaction Tests

The project I am on is building a library of reusable React components we can use across many applications. Historically, the team would test the components indirectly using application-specific tests like Cypress. However, we needed a visual and easy-to-use component testing tool for these new React components. I’d heard of some testing tools within Storybook, which we were already using. So, that’s been working out pretty well for us so far. Let’s take a look at testing React components with Storybook interaction tests.

Installing Storybook Interaction Tests

A few pieces come together to make Storybook interaction testing work. Those are @storybook/addon-interactions, providing visual feedback inside the browser, while @storybook/test-runner provides test execution and the ability to make assertions using Jest. Let’s get started by installing all the necessary tooling to get started:


yarn add @storybook/addon-interactions @storybook/test-runner @storybook/testing-library @storybook/jest --dev

Next, we need to configure storybook to run our tests in the storybook main.js file:


addons: [
  ... // other addons
  "@storybook/addon-interactions", // ⬅️ Registering interaction test addon 
],
features: {
  interactionsDebugger: true, // ⬅️  Enabling playback controls in web console
},

With this configuration enabled, when we load a component in the Storybook web console, we will see our interactions automatically executed in the “Interactions” tab of the addons menu. Now we are ready to start testing our components:

Testing an Increment and Reset Button

Let’s start with a simple React component that has two buttons, one that increments a number and one that resets the count back to zero. We want to test that, when the increment button is clicked, the text “Button clicked 0 times” is updated to reflect how many times the button has been clicked while the reset button returns the count to zero.

Testing React Components With Storybook Interaction Tests
React Component to be tested. (Click for a larger image.)

Next comes our Button component. We set up some state to hold the value of how many times the button has been clicked, two buttons that increment or reset the count, and text on the page displaying how many clicks we have.


import React, { useState } from 'react';
import clsx from 'clsx';
import './button.css';

export const Incrementor = () => {
  const [clicked, setClicked] = useState(0);

  return (
      <button type="button" data-testid="increment-button" setClicked(clicked + 1)}>
        Increment
      </button>
      <button type="button" data-testid="reset-button"> setClicked(0)}
      >
        Reset
      </button>
      Button clicked {clicked} times
 
  );
};

Our story for this component looks like this before implementing our tests for the component:


import { within, userEvent } from "@storybook/testing-library";
import { expect } from "@storybook/jest";
import { Incrementor } from "./Button";

export default {
  title: "Example/Button",
  component: Incrementor,
};

const Template = () => {
  return <Incrementor />;
};

export const Primary = Template.bind({});

Inside Storybook’s play function, we can get the canvas and find our HTML elements using findByTestId. Then we can click on them using UserEvent and assert on those actions with expect.


Primary.play = async ({ canvasElement }) => {
  const canvas = within(canvasElement);

  // Increment by one
  const incrementButton = await canvas.findByTestId("increment-button");
  await userEvent.click(incrementButton);
  await expect(
    await canvas.findByText("Button clicked 1 times")
  ).toBeInTheDocument();

  // Increment again by one
  await userEvent.click(incrementButton);
  await expect(
    await canvas.findByText("Button clicked 2 times")
  ).toBeInTheDocument();

  // Reset
  const resetButton = await canvas.findByTestId("reset-button");
  await userEvent.click(resetButton);
  await expect(
    await canvas.findByText("Button clicked 0 times")
  ).toBeInTheDocument();
};

Storybook Interaction Tests Passing in Storybook Web Console
Storybook Interaction Tests Passing in Storybook Web Console (click for larger image)

Run in CI

The last thing we needed for our project was an easy way to run our interaction tests in our code pipelines, Storybook makes that easy. Using start-server-and-test, we can build a little script that will build Storybook, serve Storybook pages locally, and run the Storybook tests. Add the necessary dependencies into your project, yarn add start-server-and-test --dev, and add the script to the package.json file:


"test-storybook:ci": "start-server-and-test storybook 6006 test:storybook",
"test:storybook": "yarn test-storybook",
Storybook Interaction Tests Passing with yarn script

Using Storybook Interaction tests, we have been able to test our React components quickly, while using a tool already in the project that developers are familiar with. We’re also able to validate our component’s functionality in our code pipelines. So far, Storybook Interactions tests have been a great addition too the project, and I would use it again.