How to test UIs with Storybook
Storybook stories are test cases for your UI components in their various states and configurations. With Storybook, you can develop and test your components at the same time, in multiple ways, with instant feedback.
Storybook tackles UI component testing from a holistic perspective, ensuring that you can not only execute component tests quickly and reliably, but that you also have well established patterns to develop, debug, maintain, and even collaborate on these tests across the development lifecycle.
Get started
If your project is using Vite, you can likely use the Vitest addon to run your component tests in Storybook. This addon is built on top of Vitest, a fast and lightweight test runner that works seamlessly with Vite.
Run this command, which will install and configure the Vitest addon and Vitest:
npx storybook add @storybook/addon-vitest
The full installation instructions, including project requirements, are available in the Vitest addon documentation.
Once your project is set up, you will see a testing widget in the bottom of your sidebar. After running tests, you will also see test status indicators on sidebar items. Additionally, many tests can be debugged with an addon panel.
If you cannot use the Vitest addon in your project, you can still run tests using the test-runner.
Next, weโll cover some key concepts of testing in Storybook.
Key concepts
Testing in Storybook is similar to other testing environments. Most of the knowledge and techniques youโve been using apply here, too. But there are also some significant improvements.
Component tests
A component test is a test which:
- Renders a component in the browser for high fidelity
- Simulates a user interacting with actual UI, like an end-to-end (E2E) test
- Only tests a unit (e.g. a single component) of UI, and can reach into the implementation to mock things or manipulate data, like a unit test
This combination of using a real browser, simulating behavior, and mocking provides a powerful means of testing the functional aspects of your UI.
In Storybook, the entire testing experience is built around component tests. This means that you can run your tests in the same environment as your stories, with the same tools and techniques.
Storybook Test
Storybook Test enables real time testing of your stories, through the Vitest addon. It uses a Vitest plugin to automatically transform your stories into real Vitest tests, which are then run with Vitestโs browser mode.
Watch mode
Get instant test feedback as you develop with watch mode. It will watch your codeโeither the component source or the testsโfor changes and automatically re-run only the relevant tests. Itโs perfect for test-driven development, where you write your tests first and then the component.
To activate watch mode, press the watch mode button (the eye icon) in the testing widget:
CI
If youโre not running Storybook Test as part of your CI, youโre missing out on the biggest benefit it provides: catching bugs on PRs before you merge them.
If you are already running vitest
as part of your CI then your stories should automatically run as tests โfor freeโ when you commit your changes to Git.
If youโre not yet running Vitest in CI, you should set that up. First by adding a new script to your package.json
:
{
"scripts": {
"test-storybook": "vitest --project=storybook"
}
}
Note that this assumes you have a Vitest project called โstorybookโ for your stories, which is the default configuration when you install Storybook Test. If youโve renamed it, adjust the script accordingly.
Next, add a new CI workflow.
If you use Github Actions that would look like:
name: Storybook Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
container:
# Make sure to grab the latest version of the Playwright image
# https://playwright.dev/docs/docker#pull-the-image
image: mcr.microsoft.com/playwright:v1.52.0-noble
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22.12.0
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm run test-storybook
If you are using a different CI provider, please consult our full CI guide for more information.
Storybook Test uses Playwright to render your stories by default. For the fastest experience, you should use a machine image that has Playwright already installed (as in the snippet above).
Coverage
Code coverage provides insight into which code is tested or not. It functions as a guardrail as you develop, to help be sure your work is covered by the proper tests.
In Storybook Test, you can see the coverage provided by your stories in two ways. First, a summary is displayed in the testing widget. Second, clicking that coverage summary will open a full, interactive report.
In the report, clicking through to a specific component shows the exact branches of code that are covered or not covered:
Each projectโs coverage report will look different, but the important things to note are:
- The overall line/branch coverage, which serves as a high-level health check.
- Specific lines/branches that are not covered, which are potential test gaps.
In our point of view, the goal is not to get 100% coverage and fill every gap, but rather to have a barometer to help you judge code/test changes, and for the gaps to inform you of key states or interactions that are untested. For example, if youโre building a prototype you might skip testing altogether, whereas in a critical app you might want to dot every i and cross every t.
Running coverage analysis can slow down your test runs, so it is turned off by default. To activate coverage, check the coverage checkbox in the testing widget.
Coverage in CI
And while weโre looking at coverage, update your CI workflow to include it:
- yarn test
+ yarn test --coverage
Why are we running all tests (yarn test
) instead of just the Storybook tests (yarn test-storybook
)? Because a coverage report is most accurate when accounting for all tests in your project, not just the stories you've written.
Seeing Storybook-specific coverage can be helpful, but in CI output, you want to see the comprehensive coverage of your project.
This way we can track the coverage change in every PR.
Those are the key concepts youโll need to test in Storybook. Now, letโs look at the different types of tests you can perform.
Types of tests
Storybook Test supports a variety of testing types to help you validate your work in multiple dimensions.
Render tests
The most important tool for testing your components in Storybook is stories that render your components in various states.
However, you might not be aware that a basic story is also a smoke test, which we call a render test. The test passes when the story renders successfully and fails when it errors.
Depending on the complexity of your components, you might be able to capture many of your key UI states this way.
Interaction tests
Render tests are a very basic kind of interaction test. To test stateful components or verify interactive behavior, you define a play function for your story:
// Replace your-framework with the name of your framework (e.g. react-vite, vue3-vite, etc.)
import type { Meta, StoryObj } from '@storybook/your-framework';
import { expect } from 'storybook/test';
import { Dialog } from './Dialog';
const meta = {
component: Dialog,
} satisfies Meta<typeof Dialog>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Opens: Story = {
play: async ({ canvas, userEvent }) => {
// Click on a button and assert that a dialog appears
const button = canvas.getByRole('button', { text: 'Open Modal' });
await userEvent.click(button);
await expect(canvas.getByRole('dialog')).toBeInTheDocument();
},
};
But play
functions can also be used for setting up state, creating spies, mocking out the network, simulating user interactions with your components, asserting output, and more. They are the meat and potatoes of testing and are the foundation for the rest of your testing journey in Storybook.
Hereโs a more complex example, which includes spying and mocking via the fn
utility.
// Replace your-framework with the name of your framework (e.g. react-vite, vue3-vite, etc.)
import type { Meta, StoryObj } from '@storybook/your-framework';
import { fn, expect } from 'storybook/test';
import { users } from '#mocks';
import { EventForm } from './EventForm';
const meta = {
component: EventForm,
} satisfies Meta<typeof EventForm>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Submits: Story = {
// Mock functions so we can manipulate and spy on them
args: {
getUsers: fn(),
onSubmit: fn(),
},
beforeEach: async ({ args }) => {
// Manipulate `getUsers` mock to return mocked value
args.getUsers.mockResolvedValue(users);
},
play: async ({ args, canvas, userEvent }) => {
const usersList = canvas.getAllByRole('listitem');
await expect(usersList).toHaveLength(4);
await expect(canvas.getAllByText('VIP')).toHaveLength(2);
const titleInput = await canvas.findByLabelText('Enter a title for your event');
await userEvent.type(titleInput, 'Holiday party');
const submitButton = canvas.getByRole('button', { text: 'Plan event' });
await userEvent.click(submitButton);
// Spy on `onSubmit` to verify that it is called correctly
await expect(args.onSubmit).toHaveBeenCalledWith({
name: 'Holiday party',
userCount: 4,
data: expect.anything(),
});
},
};
For more information on how to write interaction tests and mocks using the play
function, please see the interaction testing guide.
Accessibility tests
Storybookโs Accessibility (A11y) addon runs a set of automated checks on your stories to help ensure your components can be used by all users, regardless of ability or technology they're using. That means supporting requirements such as: keyboard navigation, screen reader support, usable color contrast, etc.
Accessibility is not only the right thing to do, but it is increasingly mandated. For example, the European accessibility act is scheduled to go into law in June 2025. Similarly in the US, laws like the Americans with Disabilities Act (ADA) and Section 508 of the Rehabilitation Act apply to many public-facing services.
To activate accessibility checks alongside your component tests, check the Accessibility checkbox in the testing widget.
Once activated, you will see accessibility failures in the sidebar.
Any failures can be debugged in the Accessibility addon panel.
Visual tests
Visual tests are the most efficient way to test your components. With the click of a button you can take snapshots of every story in your Storybook and compare those snapshots to baselines โ last known โgoodโ snapshots. Not only does this allow you to check the appearance of your components, but they are also able to check a large subset of component functionality without having to write or maintain any test code!
Storybook supports cross-browser visual testing natively usingย Chromatic, a cloud service made by the Storybook team. When you enable visual testing, every story is automatically turned into a test. This gives you instant feedback on UI bugs directly in Storybook.
Snapshot tests
In most cases, the other testing types will provide more coverage with less effort. But there are scenarios where it can be helpful to compare the rendered markup of a story against a known baseline. For example, it can help identify markup changes that trigger rendering errors.
Reusing stories in other testing tools
Stories are made to be reusable, so you can use them as test cases in popular testing tools.
End-to-end
Sometimes you need to test a full workflow, with the full running stack. In those cases, you can still use your stories by importing them within your Playwright or Cypress end-to-end (E2E) tests.
Unit
If you prefer, you can reuse your Storybook stories in a traditional testing environment, like Vitest or Jest.
More testing resources
- Vitest addon for running tests in Storybook
- Interaction testing for user behavior simulation
- Accessibility testing for accessibility
- Visual testing for appearance
- Snapshot testing for rendering errors and warnings
- Test coverage for measuring code coverage
- CI for running tests in your CI/CD pipeline
- End-to-end testing for simulating real user scenarios
- Unit testing for functionality
- Test runner to automate test execution