スナップショット
Vue School の動画でスナップショットを学ぶスナップショットテストは、関数の出力が意図せず変更されていないかを確認するのに非常に役立ちます。
スナップショットを使用すると、Vitest は指定された値のスナップショットを取得し、テストと一緒に保存されている参照スナップショットファイルと比較します。2 つのスナップショットが一致しない場合、テストは失敗します。これは、変更が予期しないものであるか、参照スナップショットを新しい結果のバージョンに更新する必要があるかのいずれかを示唆します。
スナップショットの使用
値をスナップショットとして保存するには、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/ja/book/vitest-0/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()
を呼び出すと、すべてのスナップショットがフォーマットされた 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({
serialize(val, config, indentation, depth, refs, printer) {
// `printer` は、既存のプラグインを使用して値をシリアル化する関数です。
return `Pretty foo: ${printer(val.foo)}`;
},
test(val) {
return val && Object.prototype.hasOwnProperty.call(val, 'foo');
},
});
次のようなテストを追加した場合:
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/ja/book/vitest-0/guide/snapshot
これは機能に影響を与えるものではありませんが、Jest から移行する際にコミットの差分に影響する可能性があります。
2. printBasicPrototype
のデフォルト値 {#_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 では、スナップショットファイルの作成中にカスタムメッセージが渡される場合、読みやすさのために区切り文字としてコロン :
の代わりに山括弧 >
が使用されます。
次のテストコードの例:
test('toThrowErrorMatchingSnapshot', () => {
expect(() => {
throw new Error('error');
}).toThrowErrorMatchingSnapshot('hint');
});
Jest では、スナップショットは次のようになります。
exports[`toThrowErrorMatchingSnapshot: hint 1`] = `"error"`;
Vitest では、同等のスナップショットは次のようになります。
exports[`toThrowErrorMatchingSnapshot > hint 1`] = `"error"`;