Interaction Testing
Purpose and Scope
Interaction tests in Storybook are authored inside stories, not in a separate test-only fixture. A story first renders a component with the props and context needed to put it in a meaningful initial state. The interaction test then uses the story play function to simulate user behavior such as clicking, typing, and submitting a form, followed by assertions against the resulting UI or calls. This page explains that workflow for readers who already know how to write a story and now want to turn that story into a functional test that can be debugged visually and automated later.
Sources: docs/writing-tests/interaction-testing.mdx
Storybook distinguishes a basic render test from a richer interaction test. A render test confirms that a component can render successfully in a given state, which is enough for simple static components such as a button. An interaction test goes further by exercising the component after it appears on the canvas. That means the same component example can become a test of behavior, not just appearance. The source documentation uses a login form example where one story checks the empty render and another checks form submission, illustrating how stories can represent increasingly complete user scenarios.
Sources: docs/writing-tests/interaction-testing.mdx
Relevant Source Files
- docs/writing-tests/interaction-testing.mdx — Defines the Interaction tests documentation page, explains the story plus play function testing model, introduces render tests, describes the Interactions panel, and lists the canvas query API categories derived from Testing Library.
Core Primitives
The central primitive is the story itself. In this context, a story is the unit that renders the component under test with the right inputs, providers, and surrounding state. Because the test is attached to the story, the same authored scenario can be reviewed in the browser and reused as part of automated testing. This differs from a conventional isolated unit test file where setup, rendering, and assertions often live entirely outside the component workshop. Storybook’s model keeps the tested state visible to designers, developers, and reviewers while still allowing the behavior to be validated programmatically.
Sources: docs/writing-tests/interaction-testing.mdx
The second primitive is the play function. When a component story is tested, Storybook runs the play function and validates the assertions inside it. The play function is where the test expresses user intent: type into a field, click a button, submit a form, or wait for the screen to update. It can also assert on DOM structure or on function calls. This makes the play function the bridge between an example that merely renders and an example that proves a behavior works after a realistic sequence of interactions.
Sources: docs/writing-tests/interaction-testing.mdx
The third primitive is the canvas parameter. The canvas is a queryable element containing the story under test, and Storybook provides it to the play function. Queries on the canvas come from Testing Library and follow a type plus subject naming pattern. The type communicates whether the query throws, returns null, returns an array, or awaits an element. The subject communicates what kind of accessibility or DOM signal is being used to find the element. This design encourages tests that interact with the rendered story in user-centered terms instead of reaching into implementation internals.
Sources: docs/writing-tests/interaction-testing.mdx
Execution Flow
A typical interaction test starts by choosing or writing the story state that best represents the beginning of the scenario. For example, a login form might have an empty state story and a filled or submitted state story. The author then adds a play function to the story that queries the canvas, performs user actions, and makes assertions. When Storybook tests the component, it renders the story, runs the play function, and reports whether the assertions pass. During local development, the Interactions panel in the Storybook UI lets the author preview and debug those steps alongside the rendered component.
Sources: docs/writing-tests/interaction-testing.mdx
After a story is behaving correctly in the browser, the same interaction tests can move into automation. The documentation states that they can be automated using the Vitest addon, which allows tests to run in Storybook, in a terminal, or in continuous integration environments. This sequencing is important: authors can first refine the story and play function with visual feedback, then rely on automation to prevent regressions. It keeps test authoring close to component development while still supporting the repeatability expected from a project test suite.
Sources: docs/writing-tests/interaction-testing.mdx
Querying the Canvas
The canvas query API mirrors Testing Library’s query families. Single-element queries include get, query, and find forms, while multiple-element queries use all variants. A get query throws if nothing matches and also throws if more than one element matches. A query form can return null when no element is found, making it useful when absence is expected. A find form is awaited, so it fits cases where the UI changes after an interaction. Multiple-element forms return arrays, with all-query variants distinguishing between zero matches and one or more matches.
Sources: docs/writing-tests/interaction-testing.mdx
Subjects describe the accessibility or DOM feature used to locate an element. The source page lists role, label text, placeholder text, visible text, display value, alt text, title, and test id. Prefer role or label-based queries when they match how a user or assistive technology perceives the interface. Placeholder, display value, alt text, and title queries are useful when those attributes are the meaningful way to identify an element. Test id queries exist for cases where user-facing signals are insufficient, but they should be chosen deliberately because they are less representative of real user behavior.
Sources: docs/writing-tests/interaction-testing.mdx
Compact Reference
| Component | Role in interaction testing |
|---|---|
| Story | Renders the component with the props and context needed for the initial state. |
| Render test | Confirms the story can render successfully without additional behavior checks. |
| Play function | Simulates user behavior and contains assertions that are validated during testing. |
| Canvas | Queryable element containing the story under test, passed to the play function. |
| Interactions panel | Storybook UI panel used to preview and debug interaction tests. |
| Vitest addon | Automation path for running Storybook tests in the UI, terminal, or CI. |
| Query family | When to use it |
|---|---|
| getBy / getAllBy | Use when the element or elements must already be present and absence should fail immediately. |
| queryBy / queryAllBy | Use when checking for absence or when zero matches can be an expected result. |
| findBy / findAllBy | Use when the element or elements may appear asynchronously after rendering or interaction. |
Practical Guidance and Next Steps
Keep each interaction test focused on a user-visible behavior represented by the story. If a component is static, a render test may be enough. If the component responds to input, network state, form submission, or other user actions, add a play function that demonstrates and verifies the behavior. Choose canvas queries that reflect accessible usage first, especially role and label text, then move to other subjects only when they better describe the element. Debug the sequence in the Interactions panel before depending on automated Vitest addon execution in terminal or CI workflows.
Sources: docs/writing-tests/interaction-testing.mdx
Next, read the play function documentation to understand how scripted interactions are structured, then review the broader testing overview to decide where interaction tests fit beside accessibility, visual, snapshot, coverage, and CI workflows. If a failing assertion points to missing accessible names or poor keyboard behavior, follow up with accessibility testing as well, because the same query choices that make interaction tests resilient often expose accessibility gaps.