Writing UI Tests as Stories with Storybook Interaction Testing
Learn how to write UI tests directly inside Storybook stories using the Interaction testing feature, with a concrete button‑toggle example and steps to run the tests locally and in CI.
27 Aug 2026, 22:09 UTC

The problem: UI tests live far from the component story
When a component changes, developers often have to update two separate places: the Storybook story that shows the component and the test file that asserts its behavior. Keeping them in sync is tedious, and a test failure can be hard to reproduce because the test runs in a different environment than the story you see in the browser.
Thesis: Interaction tests let you write the test inside the story, using the same render environment
Storybook’s Interaction testing feature moves the test logic into the play async function of a story. The test runs in the same iframe as the story, inherits all decorators and addons, and can be executed both in the Storybook UI and as a regular unit test in CI. This reduces context‑switching and gives immediate visual feedback when a test fails.
How Interaction tests work
Storybook 6.5+ includes the @storybook/test package. When a story exports a play function, the test runner treats it as a test step. Inside play you can use Testing Library queries (getByRole, findByText, etc.) to interact with the rendered component and make assertions with expect. The runner executes each play sequentially, logs each step, and captures a DOM snapshot after every awaited action.
Because the test runs in the story’s iframe, any decorators you have (for theming, i18n, mock providers, etc.) are automatically applied. This guarantees that the test sees exactly what the developer sees in the Storybook canvas.
Worked example: testing a button that toggles a label
Assume a simple ToggleButton component that switches its label between “Off” and “On” when clicked.
// ToggleButton.stories.js
import React from 'react';
import { ToggleButton } from './ToggleButton';
import { within } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
export default {
title: 'Example/ToggleButton',
component: ToggleButton,
};
export const Default = {};
Default.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
const button = canvas.getByRole('button', { name: /off/i });
// initial state
expect(button).toHaveTextContent('off');
// click the button
await canvas.userEvent.click(button);
// assert label changed
const updatedButton = canvas.getByRole('button', { name: /on/i });
expect(updatedButton).toHaveTextContent('on');
};
Where to run:
- Locally:
npm run storybookto start the UI, then open the story and click the “Interactions” panel. - As a unit test:
npm run test(if you have addedstorybook testto your test script) or directlynpx storybook test.
Expected checks:
- In the Storybook UI, the Interactions panel shows two steps: “initial state” and “click button”, each with a DOM snapshot.
- The test passes (green check) both in the UI and when executed via
storybook test. - If you change the component so the label does not toggle, the UI will highlight the failing step and show the snapshot where the label remained “off”.
Risks and limitations:
- The render must be synchronous. If
ToggleButtonfetched data asynchronously, you would need to mock the request orawaitthe promise insideplay; otherwise the test may race and produce flaky results. - Requires Storybook 6.5+ and the
@storybook/testing-libraryaddon. Older projects must upgrade, which can introduce breaking changes in the Storybook API. - Complex mocking (e.g., React Context providers) still needs to be added via decorators; the test runner does not automatically provide them.
Practical way to verify the setup:
- Create the story above in your project.
- Run
npx storybook test --quietand confirm the output reports “0 failed, 1 passed”. - Start Storybook (
npm run storybook), open the story, and open the Interactions pane to see the step‑by‑step logs and snapshots.
Actionable closing
If you maintain a component library with Storybook, start by converting one existing story that has a simple user interaction (a click, a form input, a toggle) into an Interaction test. Keep the test focused on a single behavior, use Testing Library queries that match how a user would find the element, and run both the UI and CI checks to confirm consistency. Over time, you’ll have a suite of UI tests that live next to their documentation, giving you faster feedback and reducing the drift between what you show and what you verify.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.