A guide to Storybook Test for interaction testing

leemeganj

Megan Lee

Posted on February 22, 2024

A guide to Storybook Test for interaction testing

Written by Nwani Victory
✏️

Since its relaunch in 2017, Storybook has become an indispensable tool for frontend developers to test and maintain modular frontend components in isolation. Storybook supports all major frontend frameworks and libraries, including React, Angular, Vue, Svelte, and a host of others.

In this tutorial, we’ll spotlight the recently released @storybook/test package, highlighting its new APIs and how it improves on the existing @storybook/jest and @storybook/testing-library packages. To follow along with the hands-on steps in this tutorial, which will demonstrate the use of the @storybook/test package within a demo Next.js application, you will need:

Let's get started.

Understanding Storybook Test

Storybook Test is a new test environment that combines the APIs of @‌storybook/jest and @‌storybook/testing-library into a single package. Its purpose is to improve the DX of Storybook, and it does so in many ways.

Consolidating these tools gives you fewer configuration requirements. You also no longer need to have the @‌storybook/jest and @‌storybook/testing-library packages installed, as you can import the test utilities from them through the @‌storybook/test package, making the project more lightweight.

The new test environment is also powered by Vitest, allowing you to import utilities from the @vitest/spy and @vitest/expect packages. Leveraging Vitest helps boost speed and introduces some new testing patterns to help decrease load times.

Since releasing Storybook Test, Storybook has encouraged developers to adopt explicit action args as a new pattern when writing actions and spies for stories. This approach improves the performance of large stories. Additionally, as explicit action args don't depend on docgen to autogenerate function mocks, they enable us to add types to the action args. They also ensure that the Play function has consistent results, which we’ll discuss more later.

The next section of this tutorial will demonstrate using explicit action args while writing interaction tests for the components within a demo application.

Testing stories with @storybook/test

To keep the focus on Storybook, we will be testing the components within a pre-built Next.js application. The components in the application were designed in the public Style Guide Starter board from the Figma community. The application also uses Tailwind CSS to style the elements.

Launch the terminal or command prompt application of your choice. Then, execute the command below to clone the pre-built application from its GitHub repository:

git clone https://github.com/vickywane/storybook-test.git
Enter fullscreen mode Exit fullscreen mode

You can also manually download the project folder from the repository. The complete code for this tutorial is within the feat/storybook-test branch of the repository.

Run the following commands to change the directory into the cloned project and install its dependencies:

#change directory into project
cd storybook-test 

#install project dependencies 
npm install
Enter fullscreen mode Exit fullscreen mode

At this point, the prebuilt application does not have Storybook installed or any stories to test. The next sections will guide you on how to set up Storybook with the latest versions that come with the @storybook/test package.

Run the following command to preview the application before you move on to adding Storybook for the individual components:

npm run dev
Enter fullscreen mode Exit fullscreen mode

As shown in the following image, navigating to http://localhost:3000 will display the demo Next.js application: Preview Of The Demo Next Js App Used In This Tutorial To Explore Storybook Test

Setting up Storybook in the prebuilt application

Execute the following init command to add the latest version of Storybook into the application via the interactive CLI:

npx storybook@latest init
Enter fullscreen mode Exit fullscreen mode

The CLI is smart enough to automatically install relevant dependencies and set up Storybook based on the existing frontend technologies being used in your project. When you perform this fresh setup of Storybook, @storybook/test is one of the packages that the init command will add to your project.

You will need to execute the following upgrade command if you were using an existing setup of the older Storybook versions:

npx storybook@next upgrade
Enter fullscreen mode Exit fullscreen mode

After the update, you will then need to install the @storybook/test package manually.

Writing stories using Storybook Test

At this point, you have Storybook fully set up in the demo Next.js application. The previous init command creates boilerplate Storybook components and examples to give you a good starting point.

Let’s create stories for the TextInput and ProjectDetailsForm components within the application. Each of these two stories will use some of the new features from the @storybook/test package.

First, create a TextInput.stories.tsx file within the stories directory to create a story for the TextInput component. Add the content of the code block below into this file. The code renders the TextInput component in the single default state:

import { userEvent, within, expect, fn } from "@storybook/test";
import TextInput from "../components/TextInput";
import { Meta, StoryObj } from "@storybook/react";

const meta: Meta<typeof TextInput> = {
 title: "TextInput",
 component: TextInput,
};

export default meta;
type Story = StoryObj<typeof TextInput>;
const MOCK_LABEL = 'Project Label'

export const Default: Story = {
 tags: ["autodocs"],
 args: {
   label: MOCK_LABEL,
   placeholder: "Enter project label name",
   type: "text",
   handleTextChange: fn(),
 },
 play: async ({args, canvasElement }) => {
   const canvas = within(canvasElement);

   const labelElement = canvas.getByTestId("label-element")
   const inputElement = canvas.getByTestId("input-element");
   await userEvent.type(inputElement, "Sample Project Name", {
     delay: 50,
   })

   await expect(labelElement.innerHTML).toBe(MOCK_LABEL)
   await expect(args.handleTextChange).toHaveBeenCalled();
 },
};
Enter fullscreen mode Exit fullscreen mode

The main focus of the story above is on the interaction tests for the TextInput component through the Play function:

  • Using the fn() method from the @storybook/test package, we are mocking and spying on the handleTextChange() action within the args object is being mocked and spied.
  • Within the tests, we expect the type() method from the canvasElement method to call the handleTextChange function as it types in mock characters into the input field.
  • We can then use the toHaveBeenCalled() assertion on the handleTextChange() action spy in the args object to confirm that handleTextChange is being called as expected.

Note that the expect, within, and userEvent testing utilities now come from the single @storybook/test package rather than the @vitest/spy, @storybook/testing-library, and @storybook/jest packages.

The image below shows the interaction tests within the TextInput story being executed: Example Of Interaction Tests With Storybook Test With the TextInput.stories.ts file, you now have your first story using the @storybook/test package to perform interaction tests!

Conclusion

Congratulations on completing this tutorial! We explored the @storybook/test package, why the Storybook team consolidated the @storybook/jest and @storybook/testing-library, and the significant performance benefits of adopting the explicit action args pattern to make your stories independent of docgen.

I hope you found this article interesting and useful as you set up or upgrade your Storybook projects to use the @storybook/test package.


Get set up with LogRocket's modern error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID.
  2. Install LogRocket via NPM or script tag. LogRocket.init() must be called client-side, not server-side.

NPM:

$ npm i --save logrocket 

// Code:

import LogRocket from 'logrocket'; 
LogRocket.init('app/id');
Enter fullscreen mode Exit fullscreen mode

Script Tag:

Add to your HTML:

<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>
Enter fullscreen mode Exit fullscreen mode

3.(Optional) Install plugins for deeper integrations with your stack:

  • Redux middleware
  • ngrx middleware
  • Vuex plugin

Get started now

💖 💪 🙅 🚩
leemeganj
Megan Lee

Posted on February 22, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related