文档
Storybook 文档

测试插件

(⚠️ 实验性)

虽然此插件处于实验阶段,但它作为 @storybook/experimental-addon-test 包发布,并且 API 可能会在将来的版本中发生更改。我们欢迎您提供反馈和贡献,以帮助改进此功能。

Storybook 的测试插件允许您直接在 Storybook 中测试组件。它通过使用 Vitest 插件将您的 故事 转换为使用 可移植故事Vitest 测试。

故事通过两种方式进行测试:烟雾测试以确保其渲染,如果定义了 播放函数,则运行该函数,并在其中进行的任何 断言 都将得到验证。

如果测试失败,侧边栏中将对其进行标记,您可以点击故事查看失败原因。您还可以以观察模式运行测试,当您对组件或故事进行更改时,它会自动重新运行测试。

安装和设置

在安装之前,请确保您的项目满足以下要求

如果您尚未使用 Storybook 8.4,您可以 将您的 Storybook 升级 到预发布版本

npx storybook@next upgrade

自动设置

运行以下命令以安装和配置插件,其中包含使用 Vitest 将您的故事作为测试运行的插件

npx storybook add @storybook/experimental-addon-test

add 命令 将安装并注册测试插件。它还将检查您项目的 Vite 和 Vitest 设置,并在必要时使用合理的默认值安装并配置它们。您可能需要调整配置以适应项目的需要。完整的配置选项可以在下面的 API 部分 中找到。

手动设置

对于某些项目设置,add 命令可能无法自动执行插件和插件设置,并要求您完成其他设置步骤。以下是操作方法

  1. 确保在您的项目中配置了 Vite 和 Vitest。
  2. 配置 Vitest 以使用 浏览器模式
  3. 在您的项目中安装插件 @storybook/experimental-addon-test,并在您的 Storybook 配置中 注册它
  4. 创建一个测试设置文件 .storybook/vitest.setup.ts。您可以使用 示例设置文件 作为指南。
  5. 调整您的 Vitest 配置以包含插件并引用设置文件。您可以使用 示例配置文件 作为指南。

框架插件

某些 Storybook 框架需要额外的设置才能使框架的功能与 Vitest 一起使用。每个框架都导出一个 Vite 插件,您可以使用它来正确配置您的项目

如果您使用的是 Next.js,请先安装 @storybook/experimental-nextjs-vite

npm install --save-dev @storybook/experimental-nextjs-vite

然后应用来自 @storybook/experimental-nextjs-vite/vite-plugin 的插件

vitest.config.ts
import { defineConfig, mergeConfig } from 'vitest/config';
import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
import { storybookNextJsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin';
 
import viteConfig from './vite.config';
 
export default mergeConfig(
  viteConfig,
  defineConfig({
    plugins: [
      storybookTest(),
      storybookNextJsPlugin(), // 👈 Apply the framework plugin here
    ],
    // ...
  })
);

上面的示例在 Vitest 配置文件中使用了框架的插件。如果您项目的配置方式是使用 Vitest 工作区文件,也可以在其中使用它。

示例配置文件

当插件自动设置时,它会为您创建或调整您的 Vitest 配置文件。如果您正在手动设置,则可以在配置项目时参考以下示例。

示例 Vitest 设置文件

Storybook 故事包含在 .storybook/preview.js|ts 中定义的配置。为了确保您的测试可以使用该配置,您可以在 Vitest 设置文件中应用它。以下是如何操作的示例

.storybook/vitest.setup.ts
import { beforeAll } from 'vitest';
// 👇 If you're using Next.js, import from @storybook/nextjs
//   If you're using Next.js with Vite, import from @storybook/experimental-nextjs-vite
import { setProjectAnnotations } from '@storybook/react';
import * as previewAnnotations from './preview';
 
const annotations = setProjectAnnotations([previewAnnotations]);
 
// Run Storybook's beforeAll hook
beforeAll(annotations.beforeAll);

setProjectAnnotations 函数是 可移植故事 API 的一部分,Vitest 插件在内部使用它将您的故事转换为测试。

示例 Vitest 配置文件

插件最简单的应用方式是将其包含在你的 Vitest 配置文件中

vitest.config.ts
import { defineConfig, mergeConfig } from 'vitest/config';
import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
// 👇 If you're using Next.js, apply this framework plugin as well
// import { storybookNextJsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin';
 
import viteConfig from './vite.config';
 
export default mergeConfig(
  viteConfig,
  defineConfig({
    plugins: [
      storybookTest({
        // This should match your package.json script to run Storybook
        // The --ci flag will skip prompts and not open a browser
        storybookScript: 'yarn storybook --ci',
      }),
      // storybookNextJsPlugin(),
    ],
    test: {
      // Glob pattern to find story files
      include: ['src/**/*.stories.?(m)[jt]s?(x)'],
      // Enable browser mode
      browser: {
        enabled: true,
        name: 'chromium',
        // Make sure to install Playwright
        provider: 'playwright',
        headless: true,
      },
      // Speed up tests and better match how they run in Storybook itself
      // https://vitest.vuejs.ac.cn/config/#isolate
      // Consider removing this if you have flaky tests
      isolate: false,
      setupFiles: ['./.storybook/vitest.setup.ts'],
    },
  })
);
示例 Vitest 工作区文件

如果你正在使用 Vitest 工作区,你可以定义一个新的工作区项目

vitest.workspace.ts
import { defineWorkspace } from 'vitest/config';
import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
// 👇 If you're using Next.js, apply this framework plugin as well
// import { storybookNextJsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin';
 
export default defineWorkspace([
  // This is the path to your existing Vitest config file
  './vitest.config.ts',
  {
    // This is the path to your existing Vite config file
    extends: './vite.config.ts',
    plugins: [
      storybookTest({
        // This should match your package.json script to run Storybook
        // The --ci flag will skip prompts and not open a browser
        storybookScript: 'yarn storybook --ci',
      }),
      // storybookNextJsPlugin(),
    ],
    test: {
      name: 'storybook',
      // Glob pattern to find story files
      include: ['src/**/*.stories.?(m)[jt]s?(x)'],
      // Enable browser mode
      browser: {
        enabled: true,
        name: 'chromium',
        // Make sure to install Playwright
        provider: 'playwright',
        headless: true,
      },
      // Speed up tests and better match how they run in Storybook itself
      // https://vitest.vuejs.ac.cn/config/#isolate
      // Consider removing this if you have flaky tests
      isolate: false,
      setupFiles: ['./.storybook/vitest.setup.ts'],
    },
  },
]);

使用

有多种方法可以使用该插件运行测试

Storybook UI

运行测试最简单的方法是通过 Storybook UI。要运行组件的所有测试,请按侧边栏底部测试模块中的“运行测试”按钮。

或者,你可以展开测试模块以单独运行特定类型的测试。对于那些具有监视模式的测试类型(在代码更改后会自动重新运行相关测试),你可以切换其开启或关闭状态。

Screenshot of test module, expanded, showing test types and watch mode toggle

如果你安装了 视觉测试插件,你将看到一个选项可以同时运行视觉测试和 组件测试

Screenshot of test module, expanded, showing Visual tests

运行测试后,你将看到故事和组件上显示其通过、失败或错误状态的状态指示器。你可以点击这些指示器以查看更多详细信息,并直接跳转到组件测试插件面板中的错误位置。该面板为你提供的组件测试提供了交互式调试器,允许你逐步执行每个模拟行为或断言。

当安装测试插件时,组件测试插件面板将替换 交互插件面板。虽然测试机制不同,但插件面板本身的功能保持不变。

测试模块还将向你显示已运行的测试总数、通过的测试数量以及失败或出错的测试数量。你可以点击失败数量以将侧边栏过滤为仅显示失败的故事。

CLI

该插件将你的故事转换为真正的 Vitest 测试,因此你可以像运行项目中的任何其他 Vitest 测试一样运行这些测试。通常,你的 package.json 中会有一个 test 脚本用于运行你的测试。

如果你还没有 test 脚本,你可以添加一个运行 Vitest 的脚本

package.json
{
  "scripts": {
    "test": "vitest"
  }
}

如果你已经有一个 test 脚本用于运行 Vitest 以外的其他内容,你可以将其调整为运行 Vitest(如上所示),或者添加一个新的运行 Vitest 的脚本

package.json
{
  "scripts": {
    "test-storybook": "vitest"
  }
}

运行该脚本时,插件将找到并运行你的基于故事的测试以及任何其他 Vitest 测试。以下是如何使用 Vitest CLI 运行测试的示例(默认情况下,在 监视模式 下)

npm run test

我们建议(并且默认配置)使用 浏览器模式Playwright 的 Chromium 浏览器运行 Vitest。浏览器模式确保你的组件在真实的浏览器环境中进行测试,这比 JSDom 或 HappyDom 等模拟更准确。这对于测试依赖于浏览器 API 或功能的组件尤其重要。

调试

虽然插件在测试时不需要 Storybook 运行,但你可能仍然希望运行 Storybook 来调试你的测试。要启用此功能,请在插件配置中提供 storybookScript 选项。当你在监视模式下运行 Vitest 时,插件将使用此脚本启动 Storybook,并在测试失败时在输出中提供故事链接。这使你能够快速跳转到 Storybook 中的故事以调试问题。

你还可以向插件配置提供 storybookUrl 选项。当你不使用监视模式且测试失败时,插件将在输出中使用此 URL 提供故事链接。这在 在 CI 或其他环境中运行测试 时非常有用,在这些环境中 Storybook 尚未运行。

Screenshot of test failure in the console, showing a failure with a link to the story

编辑器扩展

使用插件将你的故事转换为 Vitest 测试,还可以让你使用 Vitest 的 IDE 集成 来运行和调试测试。这使你能够直接从编辑器(例如 VSCode 和 JetBrains IDE)运行测试。

此屏幕截图显示了如何使用 Vitest 扩展 在 VSCode 中运行 Vitest 测试。故事上标注了测试状态,并且当测试失败时,会提供一个指向故事的链接以进行 调试

Screenshot of test failure in VSCode, showing a failure attached to a story

在 CI 中

在大多数情况下,在 CI 中运行 Storybook 测试是通过 CLI 完成的。但是,要使测试输出在测试失败时链接到已发布的 Storybook,你需要在插件配置中提供 storybookUrl 选项

以下是如何使用 GitHub Actions 的示例。其他 CI 提供商的步骤类似,但语法或配置的详细信息可能有所不同。

当 Vercel、Netlify 等服务的动作运行部署作业时,它们遵循一个发出包含在 deployment_status.target_url 下新生成的 URL 的 deployment_status 事件的模式。这是已发布的 Storybook 实例的 URL。然后,我们使用环境变量 SB_URL 将该 URL 传递给插件配置。最后,我们更新插件配置以在 storybookUrl 选项中使用该环境变量。

.github/workflows/test-storybook.yml
name: Storybook Tests
on: deployment_status
jobs:
  test:
    timeout-minutes: 60
    runs-on: ubuntu-latest
    if: github.event.deployment_status.state == 'success'
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '18.x'
      - name: Install dependencies
        run: yarn
      - name: Run Storybook tests
        run: yarn test-storybook
        env:
          SB_URL: '${{ github.event.deployment_status.target_url }}'
vitest.workspace.ts
export default defineWorkspace([
  // ...
  {
    // ...
    {
      plugins: [
        storybookTest({
          storybookScript: 'yarn storybook --ci',
          storybookUrl: process.env.SB_URL
        }),
      ],
    },
  },
])

配置测试

Vitest 插件的大部分行为配置都在 Vitest 配置和设置文件中完成。但是,你也可以在故事本身中使用 标签 定义配置,以控制如何对其进行测试。

默认情况下,插件将运行所有带有 test 标签的故事。你可以通过在插件配置中提供 tags 选项 来调整此行为。这使你能够根据标签包含、排除或跳过故事。

在此示例中,我们将 stable 标签应用于 Button 组件的所有故事,除了 ExperimentalFeatureStory,它将具有 experimental 标签

Button.stories.ts
// Replace your-framework with the framework you are using (e.g., nextjs, vue3-vite)
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta: Meta<typeof Button> = {
  component: Button,
  // 👇 Applies to all stories in this file
  tags: ['stable'],
};
export default meta;
 
type Story = StoryObj<typeof Button>;
 
export const ExperimentalFeatureStory: Story = {
  /**
   * 👇 For this particular story, remove the inherited
   *    `stable` tag and apply the `experimental` tag
   */
  tags: ['!stable', 'experimental'],
};

为了将这些标签与我们的测试行为关联起来,我们可以调整插件配置以排除 experimental 标签

vitest.workspace.ts
export default defineWorkspace([
  // ...
  {
    // ...
    {
      plugins: [
        storybookTest({
          // ...
          tags: {
            include: ['test'],
            exclude: ['experimental'],
          },
        }),
      ],
    },
  },
])

如果同一个标签同时出现在 includeexclude 数组中,则 exclude 行为优先。

与测试运行器的比较

测试运行器 需要一个正在运行的 Storybook 实例来测试你的故事,因为它会访问每个故事、执行播放函数并监听结果。但是,Vitest 插件使用 Vite 和可移植的故事将你的故事转换为测试,因此它不需要运行 Storybook 来测试你的故事。由于这种对 Vite 的依赖,插件只能与使用 Vite 的 Storybook 框架(以及 Next.js)一起使用。另一方面,测试运行器可以与任何 Storybook 框架一起使用。

测试运行器只是一个 CLI 工具。它没有提供用于运行测试的 UI,也没有编辑器扩展。但是,此插件在 Storybook 中提供了运行测试的 UI,并且它允许您使用 Vitest IDE 集成来运行和调试测试。

此外,测试运行器将您的故事作为 Jest 中的编排测试运行,并且这种编排带来了一些复杂性。相比之下,此插件将您的故事转换为真正的测试,然后使用 Vitest 运行它们,这更简单且更易于配置。

最后,由于架构更简单以及使用了 Vitest,因此对于大多数项目来说,此插件应该比测试运行器更快。我们将在未来进行更多基准测试以量化这一点。

常见问题

如果 Vitest 本身出现错误会发生什么?

有时测试可能会因 Vitest 本身发生的错误而失败。发生这种情况时,Storybook UI 中的测试模块会向您发出错误警报,您可以点击链接查看完整错误信息。错误也会记录到控制台。

Screenshot of test module, expanded, showing Vitest error

Vitest 提供了常见错误的故障排除帮助

在多个环境中测试结果不同时会发生什么?

当您使用此插件运行测试时,它们将作为 Vitest 测试运行,并使用您在项目中设置的任何配置。默认情况下,它们将在浏览器模式下运行,使用 Playwright 的 Chromium 浏览器。有时,在插件中(或通过 CLI)运行测试时会失败,但在组件测试插件面板中查看时会通过(反之亦然)。这可能是因为测试在不同的环境中运行,这些环境可能具有不同的行为。

如何在 Storybook 中调试我的 CLI 测试?

当测试在 CLI 中失败时,该插件将尝试提供指向 Storybook 中故事的链接,以用于调试目的。

如果在监视模式下运行测试时 URL 无法正常工作,则应检查两个配置选项

  • storybookUrl:确保此 URL 正确且可访问。例如,默认值为https://127.0.0.1:6006,这可能未使用您正在使用的相同端口号。
  • storybookScript:确保此脚本正确启动了 Storybook。

如果在 CI 中运行测试时 URL 无法正常工作,则应确保在运行测试之前构建并发布了 Storybook。然后,您可以使用storybookUrl选项提供已发布 Storybook 的 URL。有关示例,请参阅在 CI 中部分。

如何确保我的测试能够在公共目录中找到资源?

如果您的故事使用公共目录中的资源,并且您没有使用默认的公共目录位置(public),则需要调整 Vitest 配置以包含公共目录。您可以通过在 Vitest 配置文件中提供publicDir选项来实现。

如何应用自定义 Vite 配置?

如果您在.storybook/main.js|ts文件中的viteFinal中定义了自定义操作,则需要将其转换为 Vitest 配置。这是因为该插件不使用 Storybook Vite 配置。

例如,要重新创建 Storybook 的 Vite 配置中的别名,您需要在 Vitest 配置中应用该别名

.storybook/main.js
import { mergeConfig } from 'vite';
 
export default {
  // ...
  viteFinal: async (viteConfig) => {
    return mergeConfig(viteConfig, {
      resolve: {
        alias: {
          '@components': '/src/components',
          // ...
        },
      },
    });
  },
};

上面的示例将 Vite 配置放置在 Vitest 配置文件中。如果您的项目是这样配置的,您也可以将其放置在 Vitest 工作区文件中。

如何将 Storybook 测试与其他测试隔离开来?

某些项目可能在其 Vite 配置中包含一个test属性。因为此插件使用的 Vitest 配置扩展了该 Vite 配置,所以test属性会被合并。这种缺乏隔离可能会导致您的 Storybook 测试出现问题。

要将您的 Storybook 测试与其他测试隔离开来,您需要将test属性从您的 Vite 配置移动到 Vitest 配置。然后,插件使用的 Vitest 配置可以安全地扩展您的 Vite 配置,而无需合并test属性。

为什么我们推荐浏览器模式?

Vitest 的浏览器模式在真实的浏览器中运行您的测试(在默认配置中,通过 Playwright 使用 Chromium)。另一种选择是模拟浏览器环境,例如 JSDom 或 HappyDom,它们与真实浏览器相比,行为可能存在差异。对于 UI 组件,它们通常依赖于浏览器 API 或功能,因此在真实浏览器中运行测试更准确。

有关更多信息,请参阅Vitest 有关有效使用浏览器模式的指南

如何使用 WebDriver 而不是 Playwright?

我们建议使用 Playwright 在浏览器中运行测试,但您也可以使用 WebDriverIO。为此,您需要调整Vitest 配置文件中的浏览器提供程序

如何使用 Chromium 以外的浏览器

我们推荐使用 Chromium,因为它最有可能与大多数用户的体验最匹配。但是,您可以通过调整Vitest 配置文件中的浏览器名称来使用其他浏览器。请注意,Playwright 和 WebDriverIO 支持不同的浏览器

如何自定义测试名称?

默认情况下,故事的导出名称会映射到测试名称。要创建更具描述性的测试描述,您可以为故事提供一个name属性。这允许您包含空格、括号或其他特殊字符。

export const Story = {
  name: 'custom, descriptive name'
};

API

导出

此插件具有以下导出项

import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'

storybookTest

类型:function

一个Vitest 插件,用于将您的故事转换为测试。它接受一个选项对象进行配置。

选项

插件使用选项对象进行配置。以下是可用的属性

configDir

类型:string

默认值:.storybook

Storybook 配置文件所在的目录,相对于当前工作目录。

如果您的Storybook 配置不在默认位置,您**必须**在此处指定位置,以便插件能够正常工作。

storybookScript

类型:string

用于运行 Storybook 的可选脚本。如果提供,Vitest 将在监视模式下运行时使用此脚本启动 Storybook。仅当storybookUrl中的 Storybook 未准备好时才会运行。

storybookUrl

类型:string

默认值:https://127.0.0.1:6006

Storybook 托管的 URL。这用于内部检查并在失败时在测试输出中提供指向故事的链接

tags

类型

{
  include: string[];
  exclude: string[];
  skip: string[];
}

默认值

{
  include: ['test'],
  exclude: [],
  skip: [],
}

要包含、排除或跳过的标签。这些标签在您的故事、元数据或预览中定义为注释。

  • include:具有这些标签的故事将被测试
  • exclude:具有这些标签的故事将不会被测试,并且不会计入测试结果
  • skip:具有这些标签的故事将不会被测试,但会计入测试结果