Test addon
(⚠️ 实验性功能)
虽然此插件是实验性的,但它以 @storybook/experimental-addon-test
包发布,并且 API 在未来版本中可能会更改。我们欢迎反馈和贡献,以帮助改进此功能。
Storybook 的 Test addon 允许您直接在 Storybook 内部测试组件。 它本身可以将您的 stories 转换为 组件测试,这些测试在真实的浏览器环境中测试组件的渲染和行为。 它还可以计算由您的 stories 提供的项目覆盖率。
如果您的项目正在使用其他测试插件,例如 视觉测试插件 或 可访问性插件,您可以将这些测试与组件测试一起运行。
当为 story 运行测试时,状态将显示在侧边栏中。 可以过滤侧边栏以仅显示失败的 stories,您可以按失败的 story 上的菜单按钮以查看调试选项。
您还可以在观察模式下运行测试,当您更改组件或 stories 时,它将自动重新运行测试。 要激活,请按测试模块中的观察模式切换(眼睛图标)。
安装和设置
在安装之前,请确保您的项目满足以下要求
- Storybook ≥ 8.5
- 使用 Vite 的 Storybook 框架(例如
vue3-vite
,react-vite
, 'sveltekit` 等),或带有 Vite 的 Storybook Next.js 框架 - Vitest ≥ 2.1
- 如果您尚未使用 Vitest,则在安装插件时将为您安装和配置它
- (可选)MSW ≥ 2.0
- 如果安装了 MSW,则它必须是 v2.0.0 或更高版本,以免与 Vitest 的依赖项冲突
与 Next.js 一起使用 — Test addon 在 Next.js ≥ 14.1 项目中受支持,但您必须使用 @storybook/experimental-nextjs-vite
框架。 当您运行下面的设置命令时,如果您尚未安装和使用该框架,系统将提示您安装和使用。
如果您尚未使用 Storybook 8.5,您可以升级您的 Storybook到预发布版本
npx storybook@next upgrade
自动设置
运行以下命令来安装和配置插件,其中包含使用 Vitest 运行 stories 作为测试的插件
npx storybook add @storybook/experimental-addon-test
该 add
命令 将安装和注册测试插件。 它还将检查您项目的 Vite 和 Vitest 设置,并在必要时使用合理的默认值安装和配置它们。 您可能需要调整配置以适合您项目的需求。 完整的配置选项可以在下面的 API 部分中找到。
手动设置
对于某些项目设置,add
命令可能无法自动执行插件和插件设置,并要求您完成其他设置步骤。 以下是操作方法
- 确保在您的项目中配置了 Vite 和 Vitest。
- 配置 Vitest 以使用 浏览器模式。
- 在您的项目中安装插件
@storybook/experimental-addon-test
,并在您的 Storybook 配置中注册它。 - 创建一个测试设置文件,
.storybook/vitest.setup.ts
。 您可以使用示例设置文件作为指南。 - 调整您的 Vitest 配置以包含插件并引用设置文件。 您可以使用示例配置文件作为指南。
示例配置文件
当插件自动设置时,它将为您创建或调整 Vitest 配置文件。 如果您要手动设置,则可以在配置项目时使用以下示例作为参考。
Vitest 设置文件示例
Storybook stories 包含在 .storybook/preview.js|ts
中定义的配置。 为确保该配置可用于您的测试,您可以在 Vitest 设置文件中应用它。 以下是如何执行此操作的示例
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
函数是 portable stories API 的一部分,Vitest 插件在内部使用它将您的 stories 转换为测试。
Vitest 配置文件示例
插件最简单的应用是将其包含在您的 Vitest 配置文件中
import { defineConfig, mergeConfig } from 'vitest/config';
import { storybookTest } from '@storybook/experimental-addon-test/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,
name: 'chromium',
// Make sure to install Playwright
provider: 'playwright',
headless: true,
},
setupFiles: ['./.storybook/vitest.setup.ts'],
},
})
);
Vitest 工作区文件示例
如果您正在使用 Vitest 工作区,则可以定义一个新的工作区项目
import { defineWorkspace } from 'vitest/config';
import { storybookTest } from '@storybook/experimental-addon-test/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,
name: 'chromium',
// Make sure to install Playwright
provider: 'playwright',
headless: true,
},
setupFiles: ['./.storybook/vitest.setup.ts'],
},
},
]);
用法
有多种方法可以使用插件运行测试。
我们建议(并默认配置)在 浏览器模式下运行 Vitest,使用 Playwright 的 Chromium 浏览器。 浏览器模式确保您的组件在真实的浏览器环境中进行测试,这比像 JSDom 或 HappyDom 这样的模拟更准确。 这对于测试依赖浏览器 API 或功能的组件尤其重要。
Storybook UI
运行测试的最简单方法是通过 Storybook UI。 单击一下,您可以为项目中的所有 stories、一组 stories 或单个 story 运行多种类型的测试。
要为您整个项目运行所有测试,请按侧边栏底部测试模块中的“运行测试”按钮。
或者,您可以展开测试模块以单独运行特定类型的测试。 对于那些具有观察模式的测试类型(将在代码更改时自动重新运行相关测试),您可以打开或关闭该模式。
要为特定的 story 或一组 stories 运行测试,请按侧边栏项悬停时出现的菜单按钮(三个点)。 然后,您可以选择要运行的测试类型。
运行测试后,您现在将在 stories 和组件上看到其通过、失败或错误状态的状态指示器。 您可以按悬停 story 时的菜单按钮,以查看该 story 的测试结果。 在菜单中选择结果会将您导航到该 story 并打开相应的调试面板。 例如,如果组件测试失败,您可以直接跳转到组件测试插件面板中的失败处。 该面板为您的组件测试提供了一个交互式调试器,允许您逐步执行每个模拟的行为或断言。
当安装 Test addon 时,组件测试插件面板将替换 Interactions addon 面板。 虽然测试机制不同,但插件面板本身的功能保持不变。
测试模块还将向您显示运行的测试总数、通过的测试数以及失败或出错的测试数。 您可以按失败次数来过滤侧边栏,使其仅显示那些失败的 stories。
CLI
您还可以使用 Vitest CLI 运行测试。 我们建议在您的 package.json
中添加一个脚本,以使运行测试更容易。 以下是如何执行此操作的示例
{
"scripts": {
"test": "vitest",
"test-storybook": "vitest --project=storybook"
}
}
在此示例中,我们添加了两个脚本:test
用于运行项目中的所有测试(您可能已经拥有此脚本),以及 test-storybook
用于仅运行您的 Storybook 测试。 --project=storybook
标志告诉 Vitest 运行 Storybook 项目的测试。
然后,运行此命令以使用 Vitest CLI 运行测试(默认情况下处于 观察模式)
npm run test-storybook
调试
虽然插件在测试时不需要 Storybook 运行,但您可能仍然希望运行 Storybook 来调试测试。 要启用此功能,请在插件配置中提供 storybookScript
选项。 当您在观察模式下运行 Vitest 时,插件将使用此脚本启动 Storybook,并在测试失败时在输出中提供指向 story 的链接。 这使您可以快速跳转到 Storybook 中的 story 以调试问题。
您还可以为插件配置提供 storybookUrl
选项。 当您不使用观察模式且测试失败时,插件将在输出中使用此 URL 提供指向 story 的链接。 当在 CI 中运行测试或 Storybook 尚未运行的其他环境中时,这非常有用。
编辑器扩展
使用插件将您的 stories 转换为 Vitest 测试还使您能够使用 Vitest IDE 集成来运行和调试测试。 这允许您直接从编辑器(例如 VSCode 和 JetBrains IDE)运行测试。
此屏幕截图显示了如何在 VSCode 中使用 Vitest 扩展来运行 Vitest 测试。 Stories 使用测试状态进行注释,并且当测试失败时,会提供指向 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
选项中使用该环境变量。
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 }}'
export default defineWorkspace([
// ...
{
// ...
{
plugins: [
storybookTest({
// ...
storybookScript: 'yarn storybook --ci',
storybookUrl: process.env.SB_URL
}),
],
},
},
])
工作原理
Test addon 的工作原理是使用 Vitest 插件将您的 stories 转换为 Vitest 测试,使用 portable stories。 它还将 Vitest 配置为在 浏览器模式下运行这些测试,使用 Playwright 的 Chromium 浏览器。 由于它构建在 Vitest 之上,因此该插件需要基于 Vite 的 Storybook 框架。
Stories 通过两种方式进行测试:冒烟测试以确保它可以渲染,以及是否定义了 play 函数,则运行该函数,并验证其中进行的任何断言。
当您在 Storybook UI 中运行测试时,该插件在后台运行 Vitest 并在侧边栏中报告结果。
配置测试
可以通过两种方式配置插件运行的测试。 您可以切换要运行的测试类型,并包含、排除或跳过要测试的 stories。
切换测试类型
除了组件测试外,Test addon 还支持多种类型的测试,具体取决于您在项目中使用的其他插件。 某些测试类型(如视觉测试)是独立运行的。 其他测试类型(如可访问性)必须与组件测试一起运行。 对于这些依赖型测试类型,您可以通过按编辑按钮(铅笔图标)并在测试模块中选中或取消选中要运行的测试类型来打开或关闭它们。
请注意,您可能没有所有图片中的测试类型,具体取决于您安装的插件。
您还可以在 story 或 stories 组的侧边栏项目菜单中访问编辑模式
请注意,在菜单的编辑模式中切换测试类型会影响所有测试,而不仅仅是所选 story 或 stories 组的测试。 它旨在方便快速打开或关闭测试类型。
包含、排除或跳过测试
您可以使用标签来包含、排除或跳过要测试的 stories。 包含的 stories 会被测试,排除的 stories 不会被测试,跳过的 stories 不会被测试,但会计算在测试结果中。
默认情况下,插件将运行所有带有 test
标签的 stories。 您可以通过在插件配置中提供 tags
选项 来调整此行为。 这允许您根据 stories 的标签来包含、排除或跳过 stories。
在此示例中,我们将 stable
标签应用于 Button 组件的所有 stories,但 ExperimentalFeatureStory 除外,它将具有 experimental
标签
// 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
标签
export default defineWorkspace([
// ...
{
// ...
{
plugins: [
storybookTest({
// ...
tags: {
include: ['test'],
exclude: ['experimental'],
},
}),
],
},
},
])
如果同一个标签同时位于 include
和 exclude
数组中,则 exclude
行为优先。
与测试运行器的比较
与 测试运行器 不同,Vitest 插件将你的 stories 转换为使用 Vite 和可移植 stories 的测试,因此它不需要运行 Storybook 来测试你的 stories。由于这种对 Vite 的依赖,该插件只能与使用 Vite 的 Storybook 框架(以及 Next.js)一起使用。另一方面,测试运行器可以与任何 Storybook 框架一起使用。
测试运行器只是一个 CLI 工具。它没有用于运行测试的 UI,也没有编辑器扩展。然而,该插件在 Storybook 中提供了一个 UI 用于运行测试,并使你能够使用 Vitest IDE 集成来运行和调试测试。
此外,测试运行器在 Jest 中将你的 stories 作为编排测试运行,而这种编排带来了一些复杂性。相比之下,此插件将你的 stories 转换为真正的测试,然后使用 Vitest 运行它们,这更简单且更可配置。
最后,由于更简单的架构和 Vitest 的使用,对于大多数项目来说,此插件应该比测试运行器更快。我们将在未来进行更多基准测试以量化这一点。
常见问题
如果 Vitest 本身发生错误会怎样?
有时,测试可能会由于 Vitest 本身内部的错误而失败。发生这种情况时,Storybook UI 中的测试模块将提醒你注意错误,你可以单击链接以查看完整错误信息。错误也会记录到控制台中。
Vitest 提供了 常见错误的故障排除帮助。
当在多个环境中出现不同的测试结果时会怎样?
当你使用此插件运行测试时,它们将作为 Vitest 测试运行,并使用你在项目中设置的任何配置。默认情况下,它们将在浏览器模式下运行,使用 Playwright 的 Chromium 浏览器。有时,在插件中(或通过 CLI)运行时,测试会失败,但在组件测试插件面板中查看时则会通过(反之亦然)。这可能是因为测试在不同的环境中运行,这些环境可能具有不同的行为。
如何在 Storybook 中调试我的 CLI 测试?
当 CLI 中的测试失败时,插件将尝试提供指向 Storybook 中 story 的链接,以用于调试目的。
如果 URL 在监视模式下运行测试时不起作用,你应该检查两个配置选项
storybookUrl
:确保此 URL 正确且可访问。例如,默认值是https://127.0.0.1:6006
,这可能与你正在使用的端口号不同。storybookScript
:确保此脚本正确启动 Storybook。
如果 URL 在 CI 中运行测试时不起作用,你应该确保在运行测试之前构建和发布 Storybook。然后,你可以使用 storybookUrl
选项提供指向已发布的 Storybook 的 URL。有关示例,请参阅 In CI 部分。
如何确保我的测试可以在 public 目录中找到资源?
如果你的 stories 使用 public 目录中的资源,并且你没有使用默认的 public 目录位置 (public
),你需要调整 Vitest 配置以包含 public 目录。你可以通过在 Vitest 配置文件中提供 publicDir
选项来做到这一点。
如何将 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 支持不同的浏览器。
如何自定义测试名称?
默认情况下,story 的导出名称映射到测试名称。要创建更具描述性的测试描述,你可以为 story 提供一个 name
属性。这允许你包含空格、括号或其他特殊字符。
export const Story = {
name: 'custom, descriptive name'
};
如何修复 m.createRoot is not a function
错误?
当在使用 React 版本 18 以外版本的项目中使用插件时,可能会发生此错误。要解决此问题,你可以提供别名以确保使用正确的 React 版本。以下是在 Vitest 配置文件中执行此操作的示例
import { defineConfig } from 'vitest/config';
export default defineConfig({
// ...
resolve: {
alias: {
"@storybook/react-dom-shim": "@storybook/react-dom-shim/dist/react-16",
},
},
});
API
导出
此插件具有以下导出
import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'
storybookTest
类型: function
一个 Vitest 插件,用于将你的 stories 转换为测试。它接受一个 选项对象进行配置。
选项
该插件使用选项对象进行配置。以下是可用的属性
configDir
类型: string
默认值: .storybook
Storybook 配置所在的目录,相对于当前工作目录。
如果你的 Storybook 配置 不在默认位置,你必须在此处指定位置,以便插件可以正常运行。
storybookScript
类型: string
用于运行 Storybook 的可选脚本。如果提供,Vitest 将在监视模式下运行时使用此脚本启动 Storybook。仅当 storybookUrl
中的 Storybook 尚不可用时运行。
storybookUrl
类型: string
默认值: https://127.0.0.1:6006
Storybook 托管的 URL。这用于内部检查,并提供一个 指向测试失败时 story 的链接。
tags
类型
{
include: string[];
exclude: string[];
skip: string[];
}
默认值
{
include: ['test'],
exclude: [],
skip: [],
}
标签,用于包含、排除或跳过。这些标签在你的 story、meta 或 preview 中定义为注解。
include
:具有这些标签的 stories 将被测试exclude
:具有这些标签的 stories 将不会被测试,并且不会计入测试结果skip
:具有这些标签的 stories 将不会被测试,并且将计入测试结果