David Ekanem In love with technology and discussions around technology.

How to do interaction testing with React 18 and Storybook

6 min read 1810

How to do interaction testing with React 18 and Storybook

In this article, we’ll focus on testing the interactions of our components while using React 18 and Storybook. Before we get started, you may want to read this introductory article from Chak Shun Yu that explores React 18’s new APIs, and the blog post announcing of React 18, which provides in-depth insight into the new, opt-in concurrent rendering capabilities of React 18.

What is interaction testing?

Interaction testing involves testing interactions within React components. These tests simulate user actions within the application, such as interacting with the components by clicking buttons or typing into input components, and the behavior of the application after such interactions.

In modern applications, components hold a lot more responsibility than they once did because developers have begun including things like data fetching operations and state management tools inside components. With interaction tests, we can verify the functional aspects of our application’s user interface more effectively.

How does Storybook help with interaction testing?

Storybook is an open source tool that lets you build UI components and pages in isolation. It allows you to build documentation for components, which enables easy reuse of components and allows them to be tested visually, which helps prevent bugs.

To get started with Storybook, we need to run the Storybook CLI in an existing project’s root directory. In this case, our project is a React application, created using Create React App.

Run the commands below, to bootstrap the React application and add a story.

Create the React application:

npx create-react-app reactstorybook -template typescript
yarn start

Open up another terminal window and initialize Storybook in our project’s root directory.

npx storybook init
yarn storybook

Once the initialization is done, Storybook should start locally and output an address. Depending on your computer’s configuration, the Storybook environment may open automatically in the browser.

Welcome to Storybook introduction

A story is a component that accepts a set of arguments, which describe how to render the component. It essentially captures the rendered state of a UI component.

After initializing Storybook in our Create React App, Storybook creates example components that demonstrate the type of components that can be built with Storybook, i.e., button, header, and page.

The example components outline how Storybook manages state for different stories. The stories for the button component shown on Storybook UI can be found in our code editor.

In the root directory of the React application, open the terminal window and input the following command to view the code for the Button component and its stories.

code src/stories/Button.stories.tsx 
code src/stories/Button.tsx

One key importance of stories from Storybook is to enable the developer to keep track of how the UI looks as changes are made. This helps prevent accidental regressions, which Timothy Vernon talks more about in his post, which provides developers with testing strategies for dealing with visual regressions using Jest.

Methods of testing components in Storybook

Storybook provides an environment for testing components in isolation. It’s important to test the stories that have been written because it helps prevent UI bugs over time.



Storybook comes with a variety of tools that enable testing the UI, mocking data or dependencies, and even mocking network requests. The test runner is one of such tools. It enables developers to automatically test an entire Storybook and catch broken stories.

There are various testing methods available to developers when using Storybook for testing.

Visual regression tests

Visual regression tests assist in catching bugs in UI appearance. It works by taking screenshots of every story and comparing them to identify changes. Storybook is great for running visual tests, as every story is a test specification.

Visual regression tests should not be confused with snapshot tests, because snapshots work by comparing the rendered markup of every story against known baselines. Snapshot tests aren’t as accurate as visual regression tests because they compare blobs of HTML instead of what is displayed to the user. This can lead to false positives, as code changes don’t yield visual changes.

Accessibility tests

Quality assurance professionals rely on accessibility tests to catch accessibility variations that have been committed during application development. Accessibility tests rely on the Web Content Accessibility Guidelines (WCAG) rules to audit the rendered DOM. If the elements in the rendered DOM aren’t in line with WCAG rules, accessibility violations are thrown.

Storybook provides a very handy tool for testing for accessibility in an application with the a11y add-on.

How to perform interaction testing in React 18 with Storybook

Now, it’s time to look at how interaction testing works when it comes to Storybook and React 18.

As previously stated, interaction testing tests interactions with React components. In interaction tests, the goal is to pretend to be the user, which involves actions such as typing and clicking buttons, and making sure that the expected output is received.

The main goal of a React component is to render the UI given a set of props, but there are also complex components that track application state.

The play function included in version 6.4 of Storybook executes a story once it is loaded. The play function is key to testing our components; with the play function, you enable the ability to simulate a user’s behavior. The test runner can then be used to confirm that the component renders correctly and check that the interaction tests with the play function pass.

There are two key packages you’ll need to install for this to work:

Opening up the terminal in the root directory of our project, install the listed packages.


More great articles from LogRocket:


yarn add -dev @storybook/testing-library @storybook/jest @storybook/test-runner [email protected]

If you’re using an older version of Storybook, check your package.json to confirm that the package @storybook/addon-interactions is available in devDependencies. If it isn’t, install the package with the --dev flag, as it is integral to setting up interaction tests in Storybook.

How to update your Storybook configuration with the interactions add-on

module.exports = {
  stories: ["../src/**/*.stories.mdx", "../src/**/*[email protected](js|jsx|ts|tsx)"],
  addons: [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/addon-interactions", // Addon has to be registered
    "@storybook/preset-create-react-app",
  ],
  features: {
    interactionsDebugger: true, // enable playback controls
  },
  framework: "@storybook/react",
  core: {
    builder: "@storybook/builder-webpack5",
  },
};

Creating an interaction test with Storybook and the play function

The play function holds the logic for the test and is connected to the story we want to test. Let’s look at how to set up an interaction test with Storybook and the play function.

We rely on the @storybook/testing-library and @storybook/jest packages to help with this.

Create a Storybook component

In order to experience what’s going on behind the scenes, we’ll create a form component with two states:

  1. One state where the form has no inputs
  2. One state handled by the play function to fill in the inputs using interactions

In the stories directory, create the files Form.tsx and Form.stories.tsx.

touch src/stories/Form.tsx
touch src/stories/form.css
touch src/stories/Form.stories.tsx

Form.tsx is our first component. In that file, paste the following code:

import React from "react";
import { Button } from "./Button";
import "./form.css";

interface FormProps {
  placeholder?: string;
  label?: string;
  name?: string;
}

export const Form = ({
  placeholder = "Enter your email",
  ...props
}: FormProps) => {
  const [details, setDetails] = React.useState(false);

  const onClick = () => {
    setDetails(true);
  };

  return (
    <div className="form-wrapper">
      <input
        autoComplete="off"
        placeholder="Enter your email address"
        className={"storybook-input"}
        name="email"
        aria-label="email"
        id="email"
        data-testid="email"
        {...props}
      />
      <input
        autoComplete="off"
        placeholder="Enter your password"
        className={"storybook-input"}
        name="password"
        id="password"
        data-testid="password"
        {...props}
      />
      <Button label="Submit" onClick={() => onClick()} />
      {details === true ? (
        <p>
          Get ready to experience great development experience with storybook
        </p>
      ) : (
        ""
      )}
    </div>
  );
};

Create our CSS file.

code src/stories/form.css
.form-wrapper {
  padding: 5rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  width: 30%;
}

Creating our component’s initial state

Now, that the parent form is set up, it’s time to create our component’s initial state. Let’s start by setting up a story to set the initial state of the form.

code src/stories/Form.stories.tsx
import React from "react";
import { ComponentStory, ComponentMeta } from "@storybook/react";
import { within, userEvent } from "@storybook/testing-library";
import { expect } from "@storybook/jest";
import { Form } from "./Form";

export default {
  title: "Example/Form",
  component: Form,
} as ComponentMeta<typeof Form>;

const Template: ComponentStory<typeof Form> = (args) => <Form {...args} />;

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

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

In the above code block, the base part of our story has been built.

Storybook empty form with no interactions

Adding the play function

It’s time to set up the play function that runs after a story finishes rendering. With the play function, user workflows can be tested.

// the play function comes into use
InputedForm.play = async ({ canvasElement }) => {
  // start querying the component from its root element
  const canvas = within(canvasElement);

  // the key part, where the interactions are defined
  // Simulate interactions with the component
  await userEvent.type(canvas.getByTestId("email"), "[email protected]");

  await userEvent.type(canvas.getByTestId("password"), "a-due-password");

  await userEvent.click(canvas.getByRole("button"));

  // assert DOM structure
  await expect(
    canvas.getByText(
      "Get ready to experience great development experience with storybook"
    )
  ).toBeInTheDocument();
};

With the above code block, we’ve set up the interaction tests. userEvent simulates the user’s interactions with the component, which are filling the form and submitting it.

We’ll rely on test-runner to confirm that the component renders correctly. Update your package.json scripts to enable the test runner.

  "scripts": {
    "test-storybook": "test-storybook",
  },

Run the test runner with the following command.

yarn test-storybook

Passes all yarn tests

With confirmation of our passing tests, we can navigate to our browser window that holds our Storybook UI components to observe the interaction test in action.

Interaction test in browser

Interaction tests in Storybook are similar to Testing Library’s user-events API. If you use the user-events API, you gain the ability to carry out advanced simulations of browser interactions using the fireEvent method.

Conclusion

Building complex UIs can be a difficult activity, especially when development is done among multiple team members. But building UIs with Storybook, which places a lot of importance on building small atomic components, means components can be tested more effectively.

Visual testing is one area in which Storybook excels, and with the new tools for interaction testing, we can easily see how robust the Storybook ecosystem has become.

Cut through the noise of traditional React error reporting with LogRocket

LogRocket is a React analytics solution that shields you from the hundreds of false-positive errors alerts to just a few truly important items. LogRocket tells you the most impactful bugs and UX issues actually impacting users in your React applications.

LogRocket automatically aggregates client side errors, React error boundaries, Redux state, slow component load times, JS exceptions, frontend performance metrics, and user interactions. Then LogRocket uses machine learning to notify you of the most impactful problems affecting the most users and provides the context you need to fix it.

Focus on the React bugs that matter — .

David Ekanem In love with technology and discussions around technology.

Leave a Reply