A component test is a test which:
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, component tests are built as part of a story. That story renders the component with the necessary props and context to place it in an initial state. You then use a play function to simulate user behavior like clicks, typing, and submitting a form and then assert on the end result.
You can preview and debug your component tests using the Component Tests addon panel in the Storybook UI. They can be automated using the Test addon, allowing you to run tests for your project in Storybook, terminal, or CI environments.
Make sure your project is using Storybook 8.5 or newer, then run this command, which will install and configure Storybook Test and Vitest:
npx storybook add @storybook/experimental-addon-test
The full installation instructions, including project requirements, are available in the Test addon documentation.
Every story you write can be render tested. A render test is a simple version of a component test that only tests the ability of a component to render successfully in a given state. That works fine for relatively simple, static components like a Button. But for more complex, interactive components, you can go farther.
You can simulate user behavior and assert on functional aspects like DOM structure or function calls using the play function. When a component is tested, the play function is ran and any assertions within it are validated.
In this example, the EmptyForm story tests the render of the LoginForm component and the FilledForm story tests the form submission:
// LoginForm.stories.ts
// Replace your-renderer with the name of your renderer (e.g. react, vue3)
import type { Meta, StoryObj } from '@storybook/your-renderer
import { fn, userEvent, expect } from '@storybook/test';
import { LoginForm } from './LoginForm';
const meta = {
component: LoginForm,
args: {
// 👇 Use `fn` to spy on the onSubmit arg
onSubmit: fn();
},
} satisfies Meta<typeof LoginForm>;
export default meta;
type Story = StoryObj<typeof meta>;
export const EmptyForm: Story = {};
export const FilledForm: Story = {
play: async ({ canvas }) => {
// 👇 Use `userEvent` to simulate interactions with the component
await userEvent.type(canvas.getByLabelText('Email'), '[email protected]');
await userEvent.type(canvas.getByLabelText('Password'), 'a-random-password');
await userEvent.click(canvas.getByRole('button', { name: 'Log in' }));
// 👇 Use `expect` to assert DOM structure
await expect(
canvas.getByText(
'Everything is perfect. Your account is ready and we should probably get you started!',
),
).toBeInTheDocument();
// 👇 Assert that the onSubmit arg was called
await expect(args.onSubmit).toHaveBeenCalled();
},
};
There’s a lot going on in that code sample, so let’s walk through the APIs one-by-one.
canvas