快照
從 Vue School 的影片中學習快照只要你想確保函式的輸出不會意外變更,快照測試就是一個非常有用的工具。
當使用快照時,Vitest 會擷取給定值的快照,然後將其與儲存在測試旁的參考快照檔案進行比較。如果兩個快照不符,測試就會失敗:變更可能是意外的,或者參考快照需要更新為結果的新版本。
使用快照
若要擷取值的快照,你可以從 expect()
API 使用 toMatchSnapshot()
import { , } from 'vitest'
('toUpperCase', () => {
const = ('foobar')
().()
})
第一次執行此測試時,Vitest 會建立一個快照檔案,如下所示
// Vitest Snapshot v1, https://vitest.dev.org.tw/guide/snapshot.html
exports['toUpperCase 1'] = '"FOOBAR"'
快照成品應與程式碼變更一起提交,並在程式碼檢閱過程中進行檢閱。在後續測試執行中,Vitest 會將呈現的輸出與先前的快照進行比較。如果相符,測試將通過。如果不相符,表示測試執行器在您的程式碼中找到一個應修復的錯誤,或實作已變更,且快照需要更新。
警告
使用非同步並行測試的快照時,必須使用 測試內容 中的 expect
來確保偵測到正確的測試。
內嵌快照
同樣地,您可以使用 toMatchInlineSnapshot()
將快照儲存在測試檔案中。
import { , } from 'vitest'
('toUpperCase', () => {
const = ('foobar')
().()
})
Vitest 不會建立快照檔案,而是直接修改測試檔案,將快照更新為字串
import { , } from 'vitest'
('toUpperCase', () => {
const = ('foobar')
().('"FOOBAR"')
})
這讓您可以直接看到預期的輸出,而不必在不同的檔案之間跳轉。
警告
使用非同步並行測試的快照時,必須使用 測試內容 中的 expect
來確保偵測到正確的測試。
更新快照
當接收到的值與快照不符時,測試會失敗,並顯示兩者之間的差異。當預期快照變更時,您可能想要從目前狀態更新快照。
在監控模式中,您可以在終端機中按下 u
鍵,直接更新失敗的快照。
或者,您可以在 CLI 中使用 --update
或 -u
旗標,讓 Vitest 更新快照。
vitest -u
檔案快照
呼叫 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
旗標寫回。
影像快照
也可以使用 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` is a function that serializes a value using existing plugins.
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` is a function that serializes a value using existing plugins.
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. 快照檔案中的註解標頭不同
- // Jest Snapshot v1, https://goo.gl/fbAQLP
+ // Vitest Snapshot v1, https://vitest.dev.org.tw/guide/snapshot.html
這並不會真正影響功能,但從 Jest 移轉時可能會影響您的 commit diff。
2. printBasicPrototype
預設為 false
Jest 和 Vitest 的快照都由 pretty-format
提供支援。在 Vitest 中,我們將 printBasicPrototype
預設設定為 false
,以提供更簡潔的快照輸出,而 Jest <29.0.0 中預設為 true
。
import { , } from 'vitest'
('snapshot', () => {
const = [
{
: 'bar',
},
]
// in Jest
().(`
Array [
Object {
"foo": "bar",
},
]
`)
// in Vitest
().(`
[
{
"foo": "bar",
},
]
`)
})
我們相信這對於可讀性和整體 DX 來說是更合理的預設值。如果您仍然偏好 Jest 的行為,可以變更您的設定檔
// vitest.config.js
export default defineConfig({
test: {
snapshotFormat: {
printBasicPrototype: true
}
}
})
3. 自訂訊息使用人字號 >
作為分隔符號,而非冒號 :
在建立快照檔案時傳遞自訂訊息時,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
擷取不同
('snapshot', () => {
//
// in Jest
//
(new ('error')).(`[Error: error]`)
// Jest snapshots `Error.message` for `Error` instance
(() => {
throw new ('error')
}).(`"error"`)
//
// in Vitest
//
(new ('error')).(`[Error: error]`)
(() => {
throw new ('error')
}).(`[Error: error]`)
})