加入在线会议:美国东部时间周四上午 11 点,Storybook 9 发布及 AMA
文档
Storybook Docs

Vitest 插件

Storybook 的 Vitest 插件允许您直接在 Storybook 内部测试您的组件。它本身将您的 stories 转换为组件测试,从而在真实的浏览器环境中测试组件的渲染和行为。它还可以计算您的 stories 提供的项目覆盖率

如果您的项目使用其他测试插件,例如视觉测试插件无障碍插件,您可以将这些测试与组件测试一起运行。

当对某个 Story 运行组件测试时,状态将显示在侧边栏中。侧边栏可以过滤以仅显示失败的 Stories,您可以在失败的 Story 上按下菜单按钮以查看调试选项。

您还可以在 watch 模式下运行测试,这会在您更改组件或 Stories 时自动重新运行测试。要激活此模式,请在测试小部件中按下 watch 模式切换按钮(眼睛图标)。

安装和设置

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

  • 使用 Vite 的 Storybook 框架(例如 vue3-vitereact-vitenextjs-vitesveltekit 等)
  • Vitest ≥ 3.0
    • 如果您尚未使用 Vitest,安装此插件时将为您安装和配置 Vitest
  • (可选)MSW ≥ 2.0
    • 如果安装了 MSW,其版本必须是 v2.0.0 或更高,以避免与 Vitest 的依赖冲突

与 Next.js 一起使用 — Vitest 插件支持 Next.js ≥ 14.1 项目,但您必须使用 @storybook/nextjs-vite 框架。当您运行下面的安装命令时,如果尚未安装此框架,系统将提示您安装和使用它。

自动设置

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

npx storybook add @storybook/addon-vitest

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

手动设置

对于某些项目设置,add 命令可能无法自动完成插件安装和配置,并要求您完成额外的设置步骤。以下是操作方法

  1. 确保您的项目中配置了 Vite 和 Vitest。
  2. 将 Vitest 配置为使用浏览器模式
  3. 在您的项目中安装插件 @storybook/addon-vitest,并在 Storybook 配置中注册它
  4. 创建一个测试设置文件 .storybook/vitest.setup.ts。您可以参考示例设置文件作为指导。
  5. 调整您的 Vitest 配置以包含该插件并引用设置文件。您可以参考示例配置文件作为指导。
    • 对于已有 Vitest 测试的项目,我们建议使用工作区文件来为您的 Storybook 测试和其他测试定义独立的配置。这允许您根据需要独立运行或一起运行它们。

示例配置文件

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

示例 Vitest 设置文件

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

.storybook/vitest.setup.ts
import { beforeAll } from 'vitest';
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import { setProjectAnnotations } from '@storybook/your-framework';
import * as previewAnnotations from './preview';
 
const annotations = setProjectAnnotations([previewAnnotations]);
 
// Run Storybook's beforeAll hook
beforeAll(annotations.beforeAll);

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

示例 Vitest 配置文件

该插件最简单的应用是将其包含在您的 Vitest 配置文件中

vitest.config.ts
import { defineConfig, mergeConfig } from 'vitest/config';
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
 
const dirname =
  typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
 
import viteConfig from './vite.config';
 
export default mergeConfig(
  viteConfig,
  defineConfig({
    plugins: [
      storybookTest({
        // The location of your Storybook config, main.js|ts
        configDir: path.join(dirname, '.storybook'),
        // 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',
      }),
    ],
    test: {
      // Enable browser mode
      browser: {
        enabled: true,
        // Make sure to install Playwright
        provider: 'playwright',
        headless: true,
        instances: [{ browser: 'chromium' }],
      },
      setupFiles: ['./.storybook/vitest.setup.ts'],
    },
  }),
);
示例 Vitest 工作区文件

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

vitest.workspace.ts
import { defineWorkspace } from 'vitest/config';
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
 
const dirname =
  typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
 
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({
        // The location of your Storybook config, main.js|ts
        configDir: path.join(dirname, '.storybook'),
        // 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',
      }),
    ],
    test: {
      name: 'storybook',
      // Enable browser mode
      browser: {
        enabled: true,
        // Make sure to install Playwright
        provider: 'playwright',
        headless: true,
        instances: [{ browser: 'chromium' }],
      },
      setupFiles: ['./.storybook/vitest.setup.ts'],
    },
  },
]);

用法

使用该插件运行测试有多种方式。

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

Storybook UI

最简单的运行测试方式是通过 Storybook UI。只需点击一下,您就可以为项目中所有的 stories、一组 stories 或单个 story 运行多种类型的测试。

要运行整个项目的所有测试,请按侧边栏底部测试小部件中的“运行测试”按钮。

Screenshot of testing widget, expanded, with the Run tests button highlighted

另外,您可以展开测试小部件以单独运行特定类型的测试。组件测试下列出的子类型将一起运行,包括在启用 watch 模式(当代码更改时自动重新运行相关测试)时(使用眼睛图标)。

Screenshot of testing widget, expanded, showing test types and watch mode toggle

如果您安装了视觉测试插件,您将看到一个选项,可以将视觉测试与组件测试一起运行。

Screenshot of testing widget, expanded, showing Visual tests

其他插件,例如 a11y,也可能提供可以从测试小部件运行并影响 stories 和组件状态指示器的测试类型。

要运行特定 Story 或一组 Stories 的测试,请按下鼠标悬停在侧边栏项目上时出现的菜单按钮(三个点)。然后您可以选择要运行的测试类型。

Screenshot of story sidebar item with open menu

运行测试后,您将在 stories 和组件上看到其通过、失败或错误状态的状态指示器。将鼠标悬停在 Story 上时,您可以按下菜单按钮查看该 Story 的测试结果。在菜单中选择一个结果将带您导航到该 Story 并打开相应的调试面板。例如,如果交互测试失败,您可以直接跳转到 Interactions 面板中的失败处。该面板为您的测试提供了一个交互式调试器,允许您逐步查看每个模拟的行为或断言。

测试小部件还将显示运行的总测试数、通过的测试数以及失败或出错的测试数。您可以按下失败数来过滤侧边栏,使其仅显示失败的 stories。

CLI

您还可以使用 Vitest CLI 运行测试。我们建议在 package.json 中添加一个脚本,以便更轻松地运行测试。以下是如何操作的示例

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

在此示例中,我们添加了两个脚本:test 用于运行项目中的所有测试(您可能已经有此脚本),以及 test-storybook 用于仅运行您的 Storybook 测试。--project=storybook 标志告诉 Vitest 运行 Storybook 项目的测试。

然后,运行此命令以使用 Vitest CLI 运行您的测试(默认在watch 模式下)

npm run test-storybook

调试

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

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

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

编辑器扩展

使用该插件将您的 stories 转换为 Vitest 测试,还可以使您能够使用 Vitest IDE 集成来运行和调试测试。这允许您直接从编辑器运行测试,例如 VSCode 和 JetBrains IDE。

此屏幕截图显示了如何使用 Vitest 扩展在 VSCode 中运行您的 Vitest 测试。stories 会带有测试状态的标注,并且在测试失败时,会提供指向 Story 的链接以便调试

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

在 CI 中

大多数情况下,在 CI 中运行 Storybook 测试是通过 CLI 完成的。

但是,要使测试输出在测试失败时链接到您已发布的 Storybook,您需要在插件配置中提供storybookUrl 选项。请参考CI 测试指南中的详细示例

工作原理

Vitest 插件的工作原理是使用一个 Vitest 插件将您的 stories 使用可移植 stories 转换为 Vitest 测试。它还将 Vitest 配置为在浏览器模式下运行这些测试,使用 Playwright 的 Chromium 浏览器。由于它构建在 Vitest 之上,因此该插件需要一个基于 Vite 的 Storybook 框架。

Stories 以两种方式进行测试:冒烟测试(以确保它能够渲染),以及如果定义了 play 函数,则运行该函数并验证其中进行的任何断言

当您在Storybook UI 中运行测试时,该插件会在后台运行 Vitest 并将结果报告到侧边栏中。

配置测试

通过该插件运行的测试可以通过两种方式进行配置。您可以切换运行哪些测试类型,以及包含、排除或跳过要测试的 stories。

切换测试类型

除了组件测试之外,Vitest 插件还支持多种类型的测试,具体取决于您在项目中使用的其他插件。有些测试类型(如视觉测试)是独立运行的。其他测试类型(如无障碍测试)必须与组件测试一起运行。对于这些依赖的测试类型,您可以通过勾选或取消勾选要运行的测试类型,在测试小部件中启用或禁用它们。

Screenshot of testing widget, expanded, everything is checked

请注意,根据您安装的插件,您可能不会看到图中的所有测试类型。

包含、排除或跳过测试

您可以使用标签来包含、排除或跳过要测试的 stories。包含的 stories 将被测试,排除的 stories 不会被测试且不计入测试结果,跳过的 stories 不会被测试但会计入测试结果。

默认情况下,该插件将运行所有带有 test 标签的 stories。您可以通过在插件配置中提供tags 选项来调整此行为。这允许您根据 stories 的标签来包含、排除或跳过它们。

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

Button.stories.ts
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta = {
  component: Button,
  // 👇 Applies to all stories in this file
  tags: ['stable'],
} satisfies Meta<typeof Button>;
export default meta;
 
type Story = StoryObj<typeof meta>;
 
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 实例来测试您的 stories,因为它会访问每个 Story,执行 play 函数,并监听结果。然而,Vitest 插件使用 Vite 和可移植 stories 将您的 stories 转换为测试,因此它不需要运行 Storybook 来测试您的 stories。由于依赖 Vite,该插件只能与使用 Vite 的 Storybook 框架(以及 Next.js)一起使用。另一方面,测试运行器可以与任何 Storybook 框架一起使用。

功能Vitest 插件test-runner
测试类型
- 交互测试
- 无障碍测试
- 视觉测试
- 快照测试
测试环境
- Storybook UI
- 编辑器扩展
- CLI
- 在 CI 中
适用于所有 Storybook 框架❌ (需要 Vite)
在真实浏览器环境中运行测试
计算代码覆盖率✅ (带有 addon-coverage)
需要一个正在运行或已发布的 Storybook
可扩展到其他插件

测试运行器仅是一个 CLI 工具。它没有用于运行测试的 UI,也没有编辑器扩展。然而,该插件在 Storybook 中提供了一个用于运行测试的 UI,并使您能够使用 Vitest IDE 集成来运行和调试测试。

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

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

常见问题

如果 Vitest 本身出现错误怎么办?

有时测试可能因为 Vitest 本身内部的错误而失败。发生这种情况时,Storybook UI 中的测试小部件会提醒您错误,您可以点击链接查看完整的错误信息。错误也会被记录到控制台中。

Screenshot of testing widget, expanded, showing Vitest error

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

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

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

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

当测试在 CLI 中失败时,该插件将尝试提供指向 Storybook 中 Story 的链接,以便进行调试

如果在 watch 模式下运行测试时 URL 不起作用,您应该检查两个配置选项

  • storybookUrl:确保此 URL 正确且可访问。例如,默认值为 http://localhost:6006,这可能与您使用的端口号不同。
  • storybookScript:确保此脚本正确启动 Storybook。

如果在 CI 中运行测试时 URL 不起作用,您应该确保在运行测试之前构建并发布 Storybook。然后,您可以使用 storybookUrl 选项提供已发布 Storybook 的 URL。请参阅“在 CI 中”部分的示例。

如何确保我的测试能找到 public 目录中的资产?

如果您的 stories 使用 public 目录中的资产,并且您未使用默认的 public 目录位置 (public),您需要调整 Vitest 配置以包含该 public 目录。您可以通过在Vitest 配置文件中提供 publicDir 选项来做到这一点。

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

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

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

此外,我们建议使用工作区文件来为您的 Storybook 测试和其他测试定义独立的配置。这可确保每个配置都可以根据您的需要单独运行或一起运行。

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

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

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

如何使用 WebDriver 而不是 Playwright?

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

如何使用 Chromium 以外的浏览器?

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

如何自定义测试名称?

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

Example.stories.js|ts
export const Story = {
  name: 'custom, descriptive name'
};

如何修复 m.createRoot is not a function 错误?

在使用非 React 18 版本的项目上使用此插件时,可能会出现此错误。要解决此问题,您可以提供别名以确保使用正确的 React 版本。以下是在 Vitest 配置文件中如何操作的示例

vitest.config.ts
import { defineConfig } from 'vitest/config';
 
export default defineConfig({
  // ...
  resolve: {
    alias: {
      "@storybook/react-dom-shim": "@storybook/react-dom-shim/dist/react-16",
    },
  },
});

如何修复 Error: Vitest failed to find the current suite 错误?

如果您遇到此错误,通常不是 Vitest 的问题,而是与您的 stories 转换方式有关。以下是故障排除步骤

  1. 检查完整的错误日志以获取更多上下文,特别是关于 story 转换的部分
  2. 注意 Vite 依赖优化警告(例如,“new dependencies optimized: lodash”)
  3. 如果您看到依赖优化警告,这些警告可能在执行过程中导致破坏测试的重新加载

最常见的修复方法是预优化您的依赖项。您可以通过将依赖项添加到 Vite 配置的 optimizeDeps.include 数组来实现这一点。

这可以防止测试过程中的依赖优化,以免干扰 Vitest 的测试套件管理。

API

此插件包含以下导出

storybookTest

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

storybookTest

类型:function

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

选项

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

configDir

类型:string

默认值:.storybook

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

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

storybookScript

类型:string

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

storybookUrl

类型:string

默认值:http://localhost:6006

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

tags

类型

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

默认值

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

标签,用于包含、排除或跳过。这些标签在您的 Story、meta 或 preview 中定义为注释。

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

disableAddonDocs

类型:boolean

默认值:true

在运行测试时是否禁用插件文档 MDX 解析。

当 preview 配置或 stories 导入 mdx 文件时,它们会被模拟,因为通常测试不需要它们。您只有在您的 stories 实际需要读取和解析 MDX 文件作为渲染组件的一部分时,才可能将 disableAddonDocs 设置为 false

更多测试资源