快照
通过 Vue School 视频学习快照快照测试是一个非常有用的工具,可以确保函数的输出不会发生意外变化。
使用快照时,Vitest 会获取给定值的快照,然后将其与存储在测试文件旁的参考快照文件进行比较。如果两个快照不匹配,测试将失败:这可能是意外的变化,或者参考快照需要更新为结果的新版本。
使用快照
要为值创建快照,可以使用 expect()
API 中的 toMatchSnapshot()
:
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchSnapshot();
});
首次运行此测试时,Vitest 会创建一个快照文件,如下所示:
// Vitest Snapshot v1, https://www.getbook.com/zh-cn/book/vitest-1/guide/snapshot
exports['toUpperCase 1'] = '"FOOBAR"';
快照文件应与代码更改一起提交,并作为代码审查过程的一部分进行审查。在后续的测试运行中,Vitest 会将渲染输出与之前的快照进行比较。如果它们匹配,测试将通过。如果它们不匹配,则说明测试运行器在您的代码中发现了需要修复的错误,或者实现已更改,快照需要更新。
WARNING
当在异步并发测试中使用快照时,必须使用本地 测试上下文 中的 expect
,以确保检测到正确的测试。
内联快照
同样,您可以使用 toMatchInlineSnapshot()
将快照直接存储在测试文件中。
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchInlineSnapshot();
});
Vitest 不会创建快照文件,而是直接修改测试文件,将快照更新为字符串:
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchInlineSnapshot('"FOOBAR"');
});
这允许您直接查看预期输出,而无需在不同文件之间跳转。
WARNING
当在异步并发测试中使用快照时,必须使用本地 测试上下文 中的 expect
,以确保检测到正确的测试。
更新快照
当接收到的值与快照不匹配时,测试失败并显示它们之间的差异。当快照更改是预期行为时,您可能希望从当前状态更新快照。
在监听模式下,您可以在终端中按 u
键直接更新失败的快照。
或者您可以在 CLI 中使用 --update
或 -u
标志来让 Vitest 更新快照。
vitest -u
文件快照
调用 toMatchSnapshot()
时,所有快照都会存储在格式化的快照文件中。这意味着我们需要对快照字符串中的某些字符(例如双引号 "
和反引号 `
)进行转义。同时,您可能会丢失快照内容的语法高亮(如果内容是某种语言)。
鉴于此,我们引入了 toMatchFileSnapshot()
来显式地与文件内容进行匹配。这允许您为快照文件分配任何文件扩展名,从而提高其可读性。
import { expect, it } from 'vitest';
it('render basic', async () => {
const result = renderHTML(h('div', { class: 'foo' }));
await expect(result).toMatchFileSnapshot('./test/basic.output.html');
});
它将与 ./test/basic.output.html
的内容进行比较。并且可以使用 --update
标志将其写回。
图像快照
您也可以使用 jest-image-snapshot
对图像进行快照。
npm i -D jest-image-snapshot
test('image snapshot', () => {
expect(readFileSync('./test/stubs/input-image.png')).toMatchImageSnapshot();
});
自定义序列化器
您可以添加自己的逻辑来修改快照的序列化方式。与 Jest 一样,Vitest 为内置 JavaScript 类型、HTML 元素、ImmutableJS 和 React 元素提供了默认的序列化器。
您可以使用 expect.addSnapshotSerializer
API 显式添加自定义序列化器。
expect.addSnapshotSerializer({
serialize(val, config, indentation, depth, refs, printer) {
// `printer` 是一个使用现有插件序列化值的函数。
return `Pretty foo: ${printer(val.foo, config, indentation, depth, refs)}`;
},
test(val) {
return val && Object.prototype.hasOwnProperty.call(val, 'foo');
},
});
我们还支持 snapshotSerializers 选项来隐式地添加自定义序列化器。
import { SnapshotSerializer } from 'vitest';
export default {
serialize(val, config, indentation, depth, refs, printer) {
// `printer` 是一个使用现有插件序列化值的函数。
return `Pretty foo: ${printer(val.foo, config, indentation, depth, refs)}`;
},
test(val) {
return val && Object.prototype.hasOwnProperty.call(val, 'foo');
},
} satisfies SnapshotSerializer;
import { defineConfig } from 'vite';
export default defineConfig({
test: {
snapshotSerializers: ['path/to/custom-serializer.ts'],
},
});
添加如下测试后:
test('foo snapshot test', () => {
const bar = {
foo: {
x: 1,
y: 2,
},
};
expect(bar).toMatchSnapshot();
});
您将得到以下快照:
Pretty foo: Object {
"x": 1,
"y": 2,
}
我们使用 Jest 的 pretty-format
库来序列化快照。您可以在此处阅读更多信息:pretty-format。
与 Jest 的区别
Vitest 提供了与 Jest 几乎兼容的快照功能,但有一些例外:
1. 快照文件中的注释头不同 {#_1-comment-header-in-the-snapshot-file-is-different}
- // Jest Snapshot v1, https://goo.gl/fbAQLP
+ // Vitest Snapshot v1, https://www.getbook.com/zh-cn/book/vitest-1/guide/snapshot
这实际上不会影响功能,但可能会在从 Jest 迁移时影响您的提交差异(commit diff)。
2. printBasicPrototype
默认为 false
{#_2-printbasicprototype-is-default-to-false}
Jest 和 Vitest 的快照都由 pretty-format
提供支持。在 Vitest 中,我们将 printBasicPrototype
的默认值设置为 false
,以提供更清晰的快照输出,而在 Jest <29.0.0 版本中,它的默认值为 true
。
import { expect, test } from 'vitest';
test('snapshot', () => {
const bar = [
{
foo: 'bar',
},
];
// 在 Jest 中
expect(bar).toMatchInlineSnapshot(`
Array [
Object {
"foo": "bar",
},
]
`);
// 在 Vitest 中
expect(bar).toMatchInlineSnapshot(`
[
{
"foo": "bar",
},
]
`);
});
我们认为这对于可读性和整体开发者体验(DX)来说是一个更合理的默认设置。如果您仍然喜欢 Jest 的行为,可以更改配置:
// vitest.config.js
export default defineConfig({
test: {
snapshotFormat: {
printBasicPrototype: true,
},
},
});
3. 尖括号 >
用作分隔符而不是冒号 :
用于自定义消息 {#_3-chevron-is-used-as-a-separator-instead-of-colon-for-custom-messages}
当创建快照文件时传递自定义消息时,Vitest 使用尖括号 >
作为分隔符而不是冒号 :
,以提高可读性。
对于以下示例测试代码:
test('toThrowErrorMatchingSnapshot', () => {
expect(() => {
throw new Error('error');
}).toThrowErrorMatchingSnapshot('hint');
});
在 Jest 中,快照会是:
exports[`toThrowErrorMatchingSnapshot: hint 1`] = `"error"`;
在 Vitest 中,等效的快照会是:
exports[`toThrowErrorMatchingSnapshot > hint 1`] = `[Error: error]`;
4. toThrowErrorMatchingSnapshot
和 toThrowErrorMatchingInlineSnapshot
的默认 Error
快照不同 {#_4-default-error-snapshot-is-different-for-tothrowerrormatchingsnapshot-and-tothrowerrormatchinginlinesnapshot}
import { expect, test } from 'vitest';
test('snapshot', () => {
// 在 Jest 和 Vitest 中均适用
expect(new Error('error')).toMatchInlineSnapshot(`[Error: error]`);
// Jest 会为 `Error` 实例快照 `Error.message`
// Vitest 打印与 `toMatchInlineSnapshot` 相同的值
expect(() => {
throw new Error('error');
}).toThrowErrorMatchingInlineSnapshot(`"error"`);
}).toThrowErrorMatchingInlineSnapshot(`[Error: error]`);
});