快照测试
快照测试
快照测试只不过是在给定状态下渲染组件,拍摄渲染后的 DOM 或 HTML 的快照,然后将其与之前的快照进行比较。它们创建起来很方便,但如果快照包含太多信息,维护起来可能会很困难且噪音很大。对于 UI 组件,视觉测试(更易于审查)或交互测试(专注于功能)通常是更好的选择。然而,在某些情况下,可能需要进行快照测试,例如确保正确抛出错误。
您可以在 Jest 或 Vitest 等其他测试环境的测试基础上重用故事。为了实现这一点,Storybook 提供了 Portable Stories API,它将您的故事与其注解(args、decorators、parameters 等)组合在一起,并为您的测试生成可渲染的元素。Portable Stories 可用于:
正在寻找带有 Storyshots 的快照测试?Storyshots 已被弃用,不再维护。我们建议使用 Portable Stories API。
请参阅Storyshots 文档,了解有关如何迁移测试的更多信息。
开始使用 Portable Stories
如果您正在使用 Storybook Test,您的项目已经配置为在 Vitest 中使用 Portable Stories。
如果您不使用 Storybook Test 或希望在其他测试环境进行测试,请遵循相关文档。
快照测试一个可移植故事
快照测试可重用的故事,这是一个直接的过程,使用 Portable Stories API 中的 composeStories 获取可渲染元素,渲染该元素,然后拍摄并比较快照。
此示例在 Vitest 中渲染一个 Button 组件(通过重用 Button 的一个故事),并断言渲染的 HTML 快照匹配。
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import { composeStories } from '@storybook/your-framework';
import * as stories from '../stories/Button.stories';
const { Primary } = composeStories(stories);
test('Button snapshot', async () => {
await Primary.run();
expect(document.body.firstChild).toMatchSnapshot();
});测试运行后,将插入或创建快照。然后,当您再次运行测试并且快照不匹配时,测试将失败,您将看到类似以下的输出:
FAIL src/components/ui/Button.test.ts > Button snapshot
Error: Snapshot `Button snapshot 1` mismatched
- Expected
+ Received
<div>
<button
- class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive bg-primary text-primary-foreground shadow-xs hover:bg-primary/90 h-9 px-4 py-2 has-[>svg]:px-3"
+ class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive bg-primary text-primary-foreground shadow-xs hover:bg-primary/90 h-9 px-3 py-2 has-[>svg]:px-3"
data-slot="button"
>
Button
</button>
</div>您花了多长时间才找到改变的内容?(px-4 → px-3)
这正是为什么视觉测试对于测试 UI 组件的外观如此出色。不仅可以立即发现发生了什么变化,它还测试用户将看到的实际外观,而不仅仅是应用的 CSS。
验证错误是否被抛出
既然我们知道如何进行一般的快照测试,让我们将其应用于一个常见用例:验证预期的错误是否被正确抛出。
在此示例中,我们有一个简单的 Button React 组件,出于某种原因,它接受一个 prop,doNotUseThisItWillThrowAnError,如果使用它,它将(不出所料地)抛出错误。
function Button(props) {
if (props.doNotUseThisItWillThrowAnError) {
throw new Error("I tried to tell you...")
}
return <button {...props} />
}然后,我们有一个故事,通过args应用了该 prop。它还删除了默认的dev和test标签,以分别阻止故事显示在 Storybook 侧边栏和被 Storybook Test 测试。
export const ThrowError = {
tags: ['!dev', '!test'],
args: {
doNotUseThisItWillThrowAnError: true,
},
}最后,我们在测试文件中编写一个测试,该测试断言会抛出一个带有特定消息的错误。
// @vitest-environment jsdom
import { expect, test } from "vitest";
import { composeStories } from "@storybook/react";
import * as stories from "./Button.stories";
const { ThrowError } = composeStories(stories);
test("Button throws error", async () => {
await expect(ThrowError.run()).rejects.toThrowError('I tried to tell you...');
});此示例为教学目的而简化。相同的方法可应用于更复杂的场景,例如具有无效输入的表单或模拟的网络故障。
使用 test-runner 进行快照测试
如果您无法在项目中使用可移植故事,您仍然可以使用 test-runner 运行快照测试。请遵循test-runner 文档中的说明,在您的项目中设置 test-runner 并启用快照测试。
常见问题解答
快照测试与视觉测试有什么区别?
视觉测试会捕捉故事的图像并将其与基线图像进行比较。快照测试会拍摄 DOM 或 HTML 快照并将其与 DOM 或 HTML 基线进行比较。视觉测试更适合验证外观。快照测试适用于验证非视觉输出并确保 DOM 不会更改。
更多测试资源
- Vitest addon for running tests in Storybook
- Interaction testing for user behavior simulation
- 可访问性测试用于可访问性
- Visual testing for appearance
- Test coverage for measuring code coverage
- CI for running tests in your CI/CD pipeline
- End-to-end testing for simulating real user scenarios
- Unit testing for functionality
- Test runner to automate test execution
