Asserting Function Calls in Storybook Interaction Tests

Josh Farrant

Asserting how many times a function has been called, and with what arguments, is a common task in unit tests. Despiite this, when I started writing Storybook interaction tests it wasn't immediately obvious how to actually do it.

After trawling the Storybook docs and still not finding an answer, I stumbled upon this solution through trial and error. It appears that the action argType is using a mock function behind the scenes — which makes sense when you think about it — and you can use Jest's toHaveBeenCalled matcher to assert on it.

import type { Meta, StoryObj } from '@storybook/react';
import {
  within,
  userEvent,
} from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import { Button } from './button';

const meta: Meta<typeof Button> = {
  component: Button,
  title: 'Button',
  argTypes: {
    // Use the `action` argType to mock a function
    onClick: { action: 'clicked' },
  },
};
export default meta;

type Story = StoryObj<typeof Button>;

export const Click: Story = {
  play: async ({ canvasElement, args }) => {
    const canvas = within(canvasElement);

    // Jest will see the action as a normal mock
    expect(args.onClick).not.toHaveBeenCalled();

    await userEvent.click(canvas.getByRole('button'));

    expect(args.onClick).toHaveBeenCalledTimes(1);
  },
};