快照
透過 Vue School 的影片課程學習快照測試當您想要確保函式的輸出不會意外變更時,快照測試會是一個非常有用的工具。
使用快照時,Vitest 會取得給定值的快照,然後將其與儲存在測試程式碼旁的參考快照檔案進行比較。如果兩個快照不符,測試將會失敗:這表示變更是非預期的,或者需要將參考快照更新為結果的新版本。
使用快照
要對值建立快照,您可以使用 expect()
API 中的 toMatchSnapshot()
:
function toUpperCase(str: string) {
return str;
}
// ---cut---
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchSnapshot();
});
第一次執行此測試時,Vitest 會建立一個如下所示的快照檔案:
// Vitest Snapshot v1, https://www.getbook.com/zh-tw/book/vitest-1/guide/snapshot
exports['toUpperCase 1'] = '"FOOBAR"';
快照檔案應與程式碼變更一併提交,並作為程式碼審查流程的一部分進行檢查。在後續的測試執行中,Vitest 會將產生的輸出與先前的快照進行比較。如果它們匹配,測試將會通過。如果它們不匹配,則表示測試執行器在您的程式碼中發現了一個應該修復的錯誤,或者實作已變更,需要更新快照。
WARNING
將快照與非同步並發測試一起使用時,必須使用來自本地測試上下文的 expect
,以確保偵測到正確的測試。
內嵌快照(Inline Snapshots)
同樣地,您可以使用 toMatchInlineSnapshot()
將快照直接儲存在測試檔案中。
function toUpperCase(str: string) {
return str;
}
// ---cut---
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchInlineSnapshot();
});
Vitest 不會建立快照檔案,而是會直接修改測試檔案,將快照更新為字串:
function toUpperCase(str: string) {
return str;
}
// ---cut---
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchInlineSnapshot('"FOOBAR"');
});
這可讓您直接看到預期的輸出,無需在不同的檔案之間跳轉。
WARNING
將快照與非同步並發測試一起使用時,必須使用來自本地測試上下文的 expect
,以確保偵測到正確的測試。
更新快照
當接收到的值與快照不符時,測試會失敗,並顯示兩者之間的差異。當快照變更是預期的,您可能想要從目前狀態更新快照。
在監視模式下,您可以按下終端機中的 u
鍵,直接更新失敗的快照。
或者,您可以使用 CLI 中的 --update
或 -u
標誌,讓 Vitest 更新快照。
vitest -u
檔案快照(File Snapshots)
當呼叫 toMatchSnapshot()
時,我們會將所有快照儲存在格式化的 snap 檔案(快照檔案)中。這表示我們需要在快照字串中轉義某些字元,例如雙引號 "
和反引號 ```。同時,您可能會遺失快照內容的語法高亮 (如果它們使用某種語言)。
為了改善這種情況,我們引入了 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
標誌寫回。
圖片快照(Image Snapshots)
也可以使用 jest-image-snapshot
對圖片進行快照。
npm i -D jest-image-snapshot
test('image snapshot', () => {
expect(readFileSync('./test/stubs/input-image.png')).toMatchImageSnapshot();
});
您可以在 examples/image-snapshot
範例中了解更多資訊。
自訂序列化器
您可以添加自己的邏輯來修改快照的序列化方式。與 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-tw/book/vitest-1/guide/snapshot
這並不會實際影響功能,但可能會影響從 Jest 遷移時的提交紀錄。
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',
},
];
// in Jest
expect(bar).toMatchInlineSnapshot(`
Array [
Object {
"foo": "bar",
},
]
`);
// in 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 使用 Chevron >
作為分隔符號,而非冒號 :
,以提高可讀性。
對於以下範例測試程式碼:
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';
// ---cut---
test('snapshot', () => {
//
// in Jest
//
expect(new Error('error')).toMatchInlineSnapshot(`[Error: error]`);
// Jest 對 `Error` 實例進行 `Error.message` 快照
expect(() => {
throw new Error('error');
}).toThrowErrorMatchingInlineSnapshot(`"error"`);
//
// in Vitest
//
expect(new Error('error')).toMatchInlineSnapshot(`[Error: error]`);
expect(() => {
throw new Error('error');
}).toThrowErrorMatchingInlineSnapshot(`[Error: error]`);
});