Vi
Vitest は、vi
ヘルパーを通じて便利なユーティリティ関数を提供しています。globals 設定が有効になっている場合はグローバルにアクセスできます。または、vitest
から直接インポートすることも可能です。
import { vi } from 'vitest';
モジュールのモック
このセクションでは、モジュールをモックする際に使用できる API を説明します。Vitest は require()
を使用してインポートされたモジュールのモックをサポートしていない点に注意が必要です。
vi.mock
- 型:
(path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void
- 型:
<T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void
指定された path
からインポートされるすべてのモジュールを別のモジュールで置き換えます。パス内で設定された Vite エイリアスを使用できます。vi.mock
の呼び出しはホイスティング(巻き上げ)されるため、どこで呼び出しても問題ありません。常にすべてのインポートの前に実行されます。スコープ外の変数を参照する必要がある場合は、vi.hoisted
内で定義し、vi.mock
内で参照できます。
WARNING
vi.mock
は import
キーワードを使用してインポートされたモジュールでのみ機能します。require
では機能しません。
vi.mock
を巻き上げるために、Vitest はファイルを静的に解析します。これは、vitest
パッケージから直接インポートされていない vi
(たとえば、一部のユーティリティファイルから) は使用できないことを示しています。vitest
からインポートされた vi
を使用して vi.mock
を使用するか、globals
設定オプションを有効にしてください。
Vitest は、セットアップファイル内でインポートされたモジュールをモックしません。これは、テストファイル実行時にキャッシュされるためです。テストファイルを実行する前にすべてのモジュールキャッシュをクリアするには、vi.hoisted
内で vi.resetModules()
を呼び出すことができます。
factory
関数が定義されている場合、すべてのインポートはその結果を返します。Vitest はファクトリを一度だけ呼び出し、vi.unmock
または vi.doUnmock
が呼び出されるまで、それ以降のすべてのインポートに対して結果をキャッシュします。
jest
とは異なり、ファクトリは非同期にすることができます。vi.importActual
またはファクトリを最初の引数として渡すヘルパーを使用して、内部で元のモジュールを取得できます。
Vitest 2.1 以降、ファクトリ関数の代わりに spy
プロパティを持つオブジェクトを提供できます。spy
が true
の場合、Vitest は通常どおりモジュールを自動モックしますが、エクスポートの実装をオーバーライドしません。これは、エクスポートされたメソッドが別のメソッドによって正しく呼び出されたことをアサートしたい場合に役立ちます。
import { calculator } from './src/calculator.ts';
vi.mock('./src/calculator.ts', { spy: true });
// 元の実装を呼び出しますが、
// 後で動作を検証できます
const result = calculator(1, 2);
expect(result).toBe(3);
expect(calculator).toHaveBeenCalledWith(1, 2);
expect(calculator).toHaveReturned(3);
Vitest は、より優れた IDE サポートのために、vi.mock
および vi.doMock
メソッドで文字列の代わりにモジュールプロミスもサポートしています。ファイルが移動されると、パスが更新され、importOriginal
は型が自動的に継承されます。このシグネチャを使用すると、ファクトリの戻り値の型が元のモジュールと互換性があることが強制されます (エクスポートはオプションのままです)。
// @filename: ./path/to/module.js
export declare function total(...numbers: number[]): number;
// @filename: test.js
import { vi } from 'vitest';
// ---cut---
vi.mock(import('./path/to/module.js'), async importOriginal => {
const mod = await importOriginal(); // 型は推論されます
// ^?
return {
...mod,
// 一部のエクスポートを置き換える
total: vi.fn(),
};
});
内部的には、Vitest はモジュールオブジェクトではなく文字列ベースで動作します。
ただし、tsconfig.json
で paths
エイリアスを設定して TypeScript を使用している場合、コンパイラはインポート型を正しく解決できません。 これを機能させるには、すべてのエイリアス化されたインポートを、対応する相対パスに置き換える必要があります。 例: import('@/module')
の代わりに import('./path/to/module.js')
を使用します。
WARNING
vi.mock
はファイルの先頭に巻き上げられます (言い換えれば、_移動_されます)。これは、beforeEach
または test
の内部で記述しても、実際にはそれより前に呼び出されることを意味します。
これは、ファクトリの外部で定義された変数をファクトリの内部で使用できないことも意味します。
ファクトリの内部で変数を使用する必要がある場合は、vi.doMock
を試すことができます。同じように機能しますが、巻き上げられません。その後のインポートのみをモックすることに注意してください。
vi.hoisted
メソッドで定義された変数を、vi.mock
の前に宣言されていれば参照することもできます。
import { namedExport } from './path/to/module.js';
const mocks = vi.hoisted(() => {
return {
namedExport: vi.fn(),
};
});
vi.mock('./path/to/module.js', () => {
return {
namedExport: mocks.namedExport,
};
});
vi.mocked(namedExport).mockReturnValue(100);
expect(namedExport()).toBe(100);
expect(namedExport).toBe(mocks.namedExport);
WARNING
デフォルトエクスポートを持つモジュールをモックする場合、返されるファクトリ関数オブジェクト内に default
キーを提供する必要があります。これは ES モジュール固有の注意点です。したがって、jest
は CommonJS モジュールを使用するため、jest
のドキュメントとは異なる場合があります。例:
vi.mock('./path/to/module.js', () => {
return {
default: { myDefaultKey: vi.fn() },
namedExport: vi.fn(),
// など...
};
});
モック対象ファイルと同じ階層に __mocks__
フォルダーがあり、かつファクトリが未指定の場合、Vitest は __mocks__
サブフォルダー内に同じ名前のファイルを見つけようとし、それを実際のモジュールとして使用します。依存関係をモックしている場合、Vitest はプロジェクトのルート (デフォルトは process.cwd()
) に __mocks__
フォルダーを見つけようとします。deps.moduleDirectories
設定オプションを使用して、依存関係の場所を Vitest に伝えることができます。
たとえば、次のようなファイル構造があるとします。
- __mocks__
- axios.js
- src
__mocks__
- increment.js
- increment.js
- tests
- increment.test.js
テストファイルでファクトリまたはオプションを指定せずに vi.mock
を呼び出すと、モジュールとして使用する __mocks__
フォルダー内のファイルが見つかります。
// increment.test.js
import { vi } from 'vitest';
// axios は `__mocks__/axios.js` からのデフォルトエクスポートです
import axios from 'axios';
// increment は `src/__mocks__/increment.js` からの名前付きエクスポートです
import { increment } from '../increment.js';
vi.mock('axios');
vi.mock('../increment.js');
axios.get(`/apples/${increment(1)}`);
WARNING
vi.mock
を呼び出さない場合、モジュールが自動的にモックされることはありません。Jest の自動モック動作を再現するには、setupFiles
内で必要なモジュールごとに vi.mock
を呼び出すことができます。
__mocks__
フォルダーまたはファクトリが提供されていない場合、Vitest は元のモジュールをインポートし、そのすべてのエクスポートを自動モックします。適用されるルールについては、アルゴリズムを参照してください。
vi.doMock
- 型:
(path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void
- 型:
<T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void
vi.mock
と同じですが、ファイルの先頭に巻き上げられないため、グローバルファイルスコープの変数を参照できます。モジュールの次の動的インポートはモックされます。
WARNING
これは、これが呼び出される前にインポートされたモジュールをモックしません。ESM のすべての静的インポートは常に巻き上げられることを忘れないでください。したがって、これを静的インポートの前に置いても、インポートの前に呼び出されることはありません。
vi.doMock('./increment.js'); // これはインポートステートメントの_後に_呼び出されます
import { increment } from './increment.js';
// ./increment.js
export function increment(number) {
return number + 1;
}
import { beforeEach, test } from 'vitest';
import { increment } from './increment.js';
// vi.doMock がまだ呼び出されていないため、モジュールはモックされていません
increment(1) === 2;
let mockedIncrement = 100;
beforeEach(() => {
// ファクトリ内の変数にアクセスできます
vi.doMock('./increment.js', () => ({ increment: () => ++mockedIncrement }));
});
test('次のモジュールをインポートするとモックされたモジュールがインポートされる', async () => {
// 元のインポートはモックされませんでした。vi.doMock はインポートの後に評価されるためです
expect(increment(1)).toBe(2);
const { increment: mockedIncrement } = await import('./increment.js');
// 新しい動的インポートはモックされたモジュールを返します
expect(mockedIncrement(1)).toBe(101);
expect(mockedIncrement(1)).toBe(102);
expect(mockedIncrement(1)).toBe(103);
});
vi.mocked
- 型:
<T>(obj: T, deep?: boolean) => MaybeMockedDeep<T>
- 型:
<T>(obj: T, options?: { partial?: boolean; deep?: boolean }) => MaybePartiallyMockedDeep<T>
TypeScript の型ヘルパーです。渡されたオブジェクトを返すだけです。
partial
が true
の場合、戻り値として Partial<T>
を期待します。デフォルトでは、これは TypeScript に最初のレベルの値がモックされていると信じさせるだけです。オブジェクト全体が実際にモックされていることを TypeScript に伝えるには、2 番目の引数として { deep: true }
を渡すことができます。
// example.ts
export function add(x: number, y: number): number {
return x + y;
}
export function fetchSomething(): Promise<Response> {
return fetch('https://vitest.dev/');
}
// example.test.ts
import * as example from './example';
vi.mock('./example');
test('1 + 1 は 10 に等しい', async () => {
vi.mocked(example.add).mockReturnValue(10);
expect(example.add(1, 1)).toBe(10);
});
test('部分的に正しい型付けのみで戻り値をモックする', async () => {
vi.mocked(example.fetchSomething).mockResolvedValue(new Response('hello'));
vi.mocked(example.fetchSomething, { partial: true }).mockResolvedValue({
ok: false,
});
// vi.mocked(example.someFn).mockResolvedValue({ ok: false }) // これは型エラーです
});
vi.importActual
- 型:
<T>(path: string) => Promise<T>
モジュールをインポートし、モックする必要があるかどうかのすべてのチェックをバイパスします。モジュールを部分的にモックしたい場合に役立ちます。
vi.mock('./example.js', async () => {
const axios = await vi.importActual('./example.js');
return { ...axios, get: vi.fn() };
});
vi.importMock
- 型:
<T>(path: string) => Promise<MaybeMockedDeep<T>>
すべてのプロパティ (ネストされたプロパティを含む) がモックされたモジュールをインポートします。vi.mock
と同じルールに従います。適用されるルールについては、アルゴリズムを参照してください。
vi.unmock
- 型:
(path: string | Promise<Module>) => void
モックされたレジストリからモジュールを削除します。インポートへのすべての呼び出しは、以前にモックされていたとしても、元のモジュールを返します。この呼び出しはファイルの先頭に巻き上げられるため、たとえば setupFiles
で定義されたモジュールのみをアンモックします。
vi.doUnmock
- 型:
(path: string | Promise<Module>) => void
vi.unmock
と同じですが、ファイルの先頭に巻き上げられません。モジュールの次のインポートは、モックの代わりに元のモジュールをインポートします。これは、以前にインポートされたモジュールをアンモックしません。
// ./increment.js
export function increment(number) {
return number + 1;
}
import { increment } from './increment.js';
// vi.mock が巻き上げられているため、increment はすでにモックされています
increment(1) === 100;
// これは巻き上げられ、ファクトリは 1 行目のインポートの前に呼び出されます
vi.mock('./increment.js', () => ({ increment: () => 100 }));
// すべての呼び出しはモックされ、`increment` は常に 100 を返します
increment(1) === 100;
increment(30) === 100;
// これは巻き上げられないため、他のインポートはアンモックされたモジュールを返します
vi.doUnmock('./increment.js');
// これはまだ 100 を返します。`vi.doUnmock` はモジュールを再評価しないためです
increment(1) === 100;
increment(30) === 100;
// 次のインポートはアンモックされ、`increment` はカウント + 1 を返す元の関数になりました
const { increment: unmockedIncrement } = await import('./increment.js');
unmockedIncrement(1) === 2;
unmockedIncrement(30) === 31;
vi.resetModules
- 型:
() => Vitest
すべてのモジュールのキャッシュをクリアして、モジュールレジストリをリセットします。これにより、再インポート時にモジュールが再評価されます。トップレベルのインポートは再評価できません。ローカルの状態がテスト間で競合するモジュールを分離するのに役立つ場合があります。
import { vi } from 'vitest';
import { data } from './data.js'; // 各テストの前に再評価されません
beforeEach(() => {
vi.resetModules();
});
test('状態を変更する', async () => {
const mod = await import('./some/path.js'); // 再評価されます
mod.changeLocalState('new value');
expect(mod.getLocalState()).toBe('new value');
});
test('モジュールは古い状態を持つ', async () => {
const mod = await import('./some/path.js'); // 再評価されます
expect(mod.getLocalState()).toBe('old value');
});
WARNING
モックレジストリはリセットされません。モックレジストリをクリアするには、vi.unmock
または vi.doUnmock
を使用します。
vi.dynamicImportSettled
すべてのインポートがロードされるのを待ちます。同期呼び出しがあり、それ以外に待つことができないモジュールのインポートを開始する場合に役立ちます。
import { expect, test } from 'vitest';
// Promise が返されないため、インポートを追跡できません
function renderComponent() {
import('./component.js').then(({ render }) => {
render();
});
}
test('操作が解決される', async () => {
renderComponent();
await vi.dynamicImportSettled();
expect(document.querySelector('.component')).not.toBeNull();
});
TIP
動的インポート中に別の動的インポートが開始された場合、このメソッドはそれらすべてが解決されるまで待機します。
このメソッドは、インポートが解決された後の次の setTimeout
チックも待機するため、すべての同期操作は解決されるまでに完了しているはずです。
関数とオブジェクトのモック
このセクションでは、メソッドモックの操作方法と、環境変数およびグローバル変数の置き換え方法について説明します。
vi.fn
- 型:
(fn?: Function) => Mock
関数のスパイを作成しますが、関数なしで開始することもできます。関数が呼び出されるたびに、その呼び出し引数、戻り値、およびインスタンスを格納します。また、メソッドを使用してその動作を操作できます。 関数が指定されていない場合、モックは呼び出されたときに undefined
を返します。
const getApples = vi.fn(() => 0);
getApples();
expect(getApples).toHaveBeenCalled();
expect(getApples).toHaveReturnedWith(0);
getApples.mockReturnValueOnce(5);
const res = getApples();
expect(res).toBe(5);
expect(getApples).toHaveNthReturnedWith(2, 5);
vi.isMockFunction
- 型:
(fn: Function) => boolean
指定されたパラメーターがモック関数であるかどうかを確認します。TypeScript を使用している場合、その型も絞り込まれます。
vi.clearAllMocks
すべてのスパイで .mockClear()
を呼び出します。これにより、モック履歴はクリアされますが、実装はデフォルトのものにリセットされません。
vi.resetAllMocks
すべてのスパイで .mockReset()
を呼び出します。これにより、モック履歴はクリアされ、実装は空の関数にリセットされます (undefined
を返します)。
vi.restoreAllMocks
すべてのスパイで .mockRestore()
を呼び出します。これにより、モック履歴はクリアされ、実装は元のものにリセットされます。
vi.spyOn
- 型:
<T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set') => MockInstance
vi.fn()
と同様に、オブジェクトのメソッドまたはゲッター/セッターにスパイを作成します。モック関数を返します。
let apples = 0;
const cart = {
getApples: () => 42,
};
const spy = vi.spyOn(cart, 'getApples').mockImplementation(() => apples);
apples = 1;
expect(cart.getApples()).toBe(1);
expect(spy).toHaveBeenCalled();
expect(spy).toHaveReturnedWith(1);
TIP
afterEach
内で vi.restoreAllMocks
を呼び出す (または test.restoreMocks
を有効にする) ことで、すべてのメソッドを元の実装に戻すことができます。これにより、元のオブジェクト記述子が復元されるため、メソッドの実装を変更できなくなります。
const cart = {
getApples: () => 42,
};
const spy = vi.spyOn(cart, 'getApples').mockReturnValue(10);
console.log(cart.getApples()); // 10
vi.restoreAllMocks();
console.log(cart.getApples()); // 42
spy.mockReturnValue(10);
console.log(cart.getApples()); // まだ 42!
TIP
ブラウザモードでエクスポートされたメソッドをスパイすることはできません。代わりに、vi.mock("./file-path.js", { spy: true })
を呼び出すことで、すべてのエクスポートされたメソッドをスパイできます。これにより、すべてのエクスポートがモックされますが、その実装はそのまま維持されるため、メソッドが正しく呼び出されたかどうかをアサートできます。
import { calculator } from './src/calculator.ts';
vi.mock('./src/calculator.ts', { spy: true });
calculator(1, 2);
expect(calculator).toHaveBeenCalledWith(1, 2);
expect(calculator).toHaveReturned(3);
そして、jsdom
または他の Node.js 環境でエクスポートをスパイすることは可能ですが、これは将来変更される可能性があります。
vi.stubEnv
- 型:
<T extends string>(name: T, value: T extends "PROD" | "DEV" | "SSR" ? boolean : string | undefined) => Vitest
process.env
および import.meta.env
の環境変数の値を変更します。vi.unstubAllEnvs
を呼び出すことで、その値を復元できます。
import { vi } from 'vitest';
// `process.env.NODE_ENV` および `import.meta.env.NODE_ENV` は
// "vi.stubEnv" を呼び出す前は "development" です
vi.stubEnv('NODE_ENV', 'production');
process.env.NODE_ENV === 'production';
import.meta.env.NODE_ENV === 'production';
vi.stubEnv('NODE_ENV', undefined);
process.env.NODE_ENV === undefined;
import.meta.env.NODE_ENV === undefined;
// 他の環境変数は変更されません
import.meta.env.MODE === 'development';
TIP
単に代入することで値を変更することもできますが、以前の値を復元するために vi.unstubAllEnvs
を使用することはできません。
import.meta.env.MODE = 'test';
vi.unstubAllEnvs
- 型:
() => Vitest
vi.stubEnv
で変更されたすべての import.meta.env
および process.env
の値を復元します。初めて呼び出されたとき、Vitest は元の値を記憶し、unstubAllEnvs
が再度呼び出されるまでそれを格納します。
import { vi } from 'vitest';
// `process.env.NODE_ENV` および `import.meta.env.NODE_ENV` は
// stubEnv を呼び出す前は "development" です
vi.stubEnv('NODE_ENV', 'production');
process.env.NODE_ENV === 'production';
import.meta.env.NODE_ENV === 'production';
vi.stubEnv('NODE_ENV', 'staging');
process.env.NODE_ENV === 'staging';
import.meta.env.NODE_ENV === 'staging';
vi.unstubAllEnvs();
// 最初の "stubEnv" 呼び出しの前に格納されていた値に復元されます
process.env.NODE_ENV === 'development';
import.meta.env.NODE_ENV === 'development';
vi.stubGlobal
- 型:
(name: string | number | symbol, value: unknown) => Vitest
グローバル変数の値を変更します。vi.unstubAllGlobals
を呼び出すことで、元の値を復元できます。
import { vi } from 'vitest';
// `innerWidth` は stubGlobal を呼び出す前は "0" です
vi.stubGlobal('innerWidth', 100);
innerWidth === 100;
globalThis.innerWidth === 100;
// jsdom または happy-dom を使用している場合
window.innerWidth === 100;
TIP
単に globalThis
または window
(jsdom または happy-dom 環境を使用している場合) に代入することで値を変更することもできますが、元の値を復元するために vi.unstubAllGlobals
を使用することはできません。
globalThis.innerWidth = 100;
// jsdom または happy-dom を使用している場合
window.innerWidth = 100;
vi.unstubAllGlobals
- 型:
() => Vitest
vi.stubGlobal
で変更された globalThis
/global
(および window
/top
/self
/parent
、jsdom または happy-dom 環境を使用している場合) のすべてのグローバル値を復元します。初めて呼び出されたとき、Vitest は元の値を記憶し、unstubAllGlobals
が再度呼び出されるまでそれを格納します。
import { vi } from 'vitest';
const Mock = vi.fn();
// IntersectionObserver は "stubGlobal" を呼び出す前は "undefined" です
vi.stubGlobal('IntersectionObserver', Mock);
IntersectionObserver === Mock;
global.IntersectionObserver === Mock;
globalThis.IntersectionObserver === Mock;
// jsdom または happy-dom を使用している場合
window.IntersectionObserver === Mock;
vi.unstubAllGlobals();
globalThis.IntersectionObserver === undefined;
'IntersectionObserver' in globalThis === false;
// 定義されていないため ReferenceError がスローされます
IntersectionObserver === undefined;
フェイクタイマー
このセクションでは、フェイクタイマーの操作方法について説明します。
vi.advanceTimersByTime
- 型:
(ms: number) => Vitest
このメソッドは、指定されたミリ秒数が経過するか、キューが空になるまで、開始されたすべてのタイマーを呼び出します。どちらか早い方です。
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersByTime(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersByTimeAsync
- 型:
(ms: number) => Promise<Vitest>
このメソッドは、指定されたミリ秒数が経過するか、キューが空になるまで、開始されたすべてのタイマーを呼び出します。どちらか早い方です。これには非同期に設定されたタイマーが含まれます。
let i = 0;
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50);
await vi.advanceTimersByTimeAsync(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersToNextTimer
- 型:
() => Vitest
次に利用可能なタイマーを呼び出します。各タイマー呼び出しの間でアサーションを行うのに役立ちます。自分でタイマーを管理するためにチェーンして呼び出すことができます。
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersToNextTimer() // log: 1
.advanceTimersToNextTimer() // log: 2
.advanceTimersToNextTimer(); // log: 3
vi.advanceTimersToNextTimerAsync
- 型:
() => Promise<Vitest>
次に利用可能なタイマーを呼び出し、非同期に設定された場合は解決されるまで待機します。各タイマー呼び出しの間でアサーションを行うのに役立ちます。
let i = 0;
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50);
await vi.advanceTimersToNextTimerAsync(); // log: 1
expect(console.log).toHaveBeenCalledWith(1);
await vi.advanceTimersToNextTimerAsync(); // log: 2
await vi.advanceTimersToNextTimerAsync(); // log: 3
vi.advanceTimersToNextFrame 2.1.0+
- 型:
() => Vitest
vi.advanceTimersByTime
と似ていますが、requestAnimationFrame
で現在スケジュールされているコールバックを実行するために必要なミリ秒数だけタイマーを進めます。
let frameRendered = false;
requestAnimationFrame(() => {
frameRendered = true;
});
vi.advanceTimersToNextFrame();
expect(frameRendered).toBe(true);
vi.getTimerCount
- 型:
() => number
待機中のタイマーの数を取得します。
vi.clearAllTimers
実行がスケジュールされているすべてのタイマーを削除します。これらのタイマーは将来実行されません。
vi.getMockedSystemTime
- 型:
() => Date | null
setSystemTime
を使用して設定されたモックされた現在の日付を返します。日付がモックされていない場合、メソッドは null
を返します。
vi.getRealSystemTime
- 型:
() => number
vi.useFakeTimers
を使用している場合、Date.now
の呼び出しはモックされます。ミリ秒単位で実際の時間を取得する必要がある場合は、この関数を呼び出すことができます。
vi.runAllTicks
- 型:
() => Vitest
process.nextTick
によってキューに入れられたすべてのマイクロタスクを呼び出します。これにより、それ自体によってスケジュールされたすべてのマイクロタスクも実行されます。
vi.runAllTimers
- 型:
() => Vitest
このメソッドは、タイマーキューが空になるまで、開始されたすべてのタイマーを呼び出します。これは、runAllTimers
中に呼び出されたすべてのタイマーが発火されることを意味します。無限の間隔がある場合、10,000 回の試行後にスローされます (fakeTimers.loopLimit
で設定可能)。
let i = 0;
setTimeout(() => console.log(++i));
const interval = setInterval(() => {
console.log(++i);
if (i === 3) {
clearInterval(interval);
}
}, 50);
vi.runAllTimers();
// log: 1
// log: 2
// log: 3
vi.runAllTimersAsync
- 型:
() => Promise<Vitest>
このメソッドは、タイマーキューが空になるまで、開始されたすべてのタイマーを非同期に呼び出します。これは、runAllTimersAsync
中に呼び出されたすべてのタイマーが、非同期タイマーであっても発火されることを意味します。無限の間隔がある場合、 10,000 回の試行後にスローされます (fakeTimers.loopLimit
で設定可能)。
setTimeout(async () => {
console.log(await Promise.resolve('result'));
}, 100);
await vi.runAllTimersAsync();
// log: result
vi.runOnlyPendingTimers
- 型:
() => Vitest
このメソッドは、vi.useFakeTimers
呼び出し後に開始されたすべてのタイマーを呼び出します。呼び出し中に開始されたタイマーは発火しません。
let i = 0;
setInterval(() => console.log(++i), 50);
vi.runOnlyPendingTimers();
// log: 1
vi.runOnlyPendingTimersAsync
- 型:
() => Promise<Vitest>
このメソッドは、vi.useFakeTimers
呼び出し後に開始されたすべてのタイマーを非同期に呼び出します。非同期タイマーであってもです。呼び出し中に開始されたタイマーは発火しません。
setTimeout(() => {
console.log(1);
}, 100);
setTimeout(() => {
Promise.resolve().then(() => {
console.log(2);
setInterval(() => {
console.log(3);
}, 40);
});
}, 10);
await vi.runOnlyPendingTimersAsync();
// log: 2
// log: 3
// log: 3
// log: 1
vi.setSystemTime
- 型:
(date: string | number | Date) => void
フェイクタイマーが有効になっている場合、このメソッドはユーザーがシステムクロックを変更するのをシミュレートします (hrtime
、performance.now
、new Date()
などの日付関連 API に影響します) が、タイマーは発火しません。フェイクタイマーが有効になっていない場合、このメソッドは Date.*
の呼び出しのみをモックします。
現在の時刻に依存するものをテストする必要がある場合に役立ちます。たとえば、コード内の Luxon の呼び出しなどです。
Date
と同じ文字列および数値引数を受け入れます。
const date = new Date(1998, 11, 19);
vi.useFakeTimers();
vi.setSystemTime(date);
expect(Date.now()).toBe(date.valueOf());
vi.useRealTimers();
vi.useFakeTimers
- 型:
(config?: FakeTimerInstallOpts) => Vitest
タイマーのモックを有効にするには、このメソッドを呼び出す必要があります。vi.useRealTimers()
が呼び出されるまで、タイマー (setTimeout
、setInterval
、clearTimeout
、clearInterval
、setImmediate
、clearImmediate
、Date
など) へのそれ以降のすべての呼び出しをラップします。
--pool=forks
を使用して node:child_process
内で Vitest を実行している場合、nextTick
のモックはサポートされていません。NodeJS は node:child_process
内で内部的に process.nextTick
を使用しており、モックされるとハングします。nextTick
のモックは、--pool=threads
を使用して Vitest を実行している場合にサポートされます。
実装は内部的に @sinonjs/fake-timers
に基づいています。
TIP
vi.useFakeTimers()
は process.nextTick
を自動的にモックしません。 ただし、toFake
引数にオプションを指定することで有効にできます: vi.useFakeTimers({ toFake: ['nextTick'] })
。
vi.isFakeTimers
- 型:
() => boolean
フェイクタイマーが有効になっている場合は true
を返します。
vi.useRealTimers
- 型:
() => Vitest
タイマーが使い果たされたら、このメソッドを呼び出してモックされたタイマーを元の実装に戻すことができます。以前にスケジュールされていたすべてのタイマーは破棄されます。
その他
Vitest が提供する便利なヘルパー関数のセットです。
vi.waitFor
- 型:
<T>(callback: WaitForCallback<T>, options?: number | WaitForOptions) => Promise<T>
コールバックが正常に実行されるのを待ちます。コールバックがエラーをスローするか、拒否されたプロミスを返した場合、成功するかタイムアウトするまで待機を続けます。
これは、非同期アクションが完了するのを待つ必要がある場合に非常に便利です。たとえば、サーバーを起動して起動するのを待つ必要がある場合などです。
import { expect, test, vi } from 'vitest';
import { createServer } from './server.js';
test('サーバーが正常に起動した', async () => {
const server = createServer();
await vi.waitFor(
() => {
if (!server.isReady) {
throw new Error('サーバーが起動していません');
}
console.log('サーバーが起動しました');
},
{
timeout: 500, // デフォルトは 1000
interval: 20, // デフォルトは 50
}
);
expect(server.isReady).toBe(true);
});
非同期コールバックでも機能します
// @vitest-environment jsdom
import { expect, test, vi } from 'vitest';
import { getDOMElementAsync, populateDOMAsync } from './dom.js';
test('要素が DOM に存在する', async () => {
// DOM の生成を開始
populateDOMAsync();
const element = await vi.waitFor(
async () => {
// 要素が存在するまで取得を試みる
const element = (await getDOMElementAsync()) as HTMLElement | null;
expect(element).toBeTruthy();
expect(element.dataset.initialized).toBeTruthy();
return element;
},
{
timeout: 500, // デフォルトは 1000
interval: 20, // デフォルトは 50
}
);
expect(element).toBeInstanceOf(HTMLElement);
});
vi.useFakeTimers
が使用されている場合、vi.waitFor
は各チェックコールバックで自動的に vi.advanceTimersByTime(interval)
を呼び出します。
vi.waitUntil
- 型:
<T>(callback: WaitUntilCallback<T>, options?: number | WaitUntilOptions) => Promise<T>
これは vi.waitFor
と似ていますが、コールバックがエラーをスローした場合、実行はすぐに中断され、エラーメッセージが受信されます。コールバックが偽の値を返した場合、真の値が返されるまで次のチェックが続行されます。これは、次のステップに進む前に何かが存在するのを待つ必要がある場合に便利です。
以下の例を見てください。vi.waitUntil
を使用して要素がページに表示されるのを待ち、その後要素に対して何かを行うことができます。
import { expect, test, vi } from 'vitest';
test('要素が正しくレンダリングされる', async () => {
const element = await vi.waitUntil(() => document.querySelector('.element'), {
timeout: 500, // デフォルトは 1000
interval: 20, // デフォルトは 50
});
// 要素に対して何かを行う
expect(element.querySelector('.element-child')).toBeTruthy();
});
vi.hoisted
- 型:
<T>(factory: () => T) => T
ES モジュールのすべての静的 import
ステートメントはファイルの先頭に巻き上げられるため、インポートが評価される前に定義されたコードは実際にはインポートの後に実行されます。
ただし、モジュールをインポートする前に日付のモックなどの副作用を呼び出すと便利な場合があります。
この制限を回避するには、静的インポートを次のように動的インポートに書き換えることができます。
callFunctionWithSideEffect()
- import { value } from './some/module.js'
+ const { value } = await import('./some/module.js')
vitest
を実行している場合、vi.hoisted
メソッドを使用することでこれを自動的に行うことができます。
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
このメソッドは、ファクトリから返された値を返します。ローカルで定義された変数に簡単にアクセスする必要がある場合は、その値を vi.mock
ファクトリで使用できます。
import { expect, vi } from 'vitest';
import { originalMethod } from './path/to/module.js';
const { mockedMethod } = vi.hoisted(() => {
return { mockedMethod: vi.fn() };
});
vi.mock('./path/to/module.js', () => {
return { originalMethod: mockedMethod };
});
mockedMethod.mockReturnValue(100);
expect(originalMethod()).toBe(100);
このメソッドは、環境がトップレベル await をサポートしていなくても非同期に呼び出すこともできます。
const promised = await vi.hoisted(async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
return response.json();
});
vi.setConfig
- 型:
RuntimeConfig
現在のテストファイルの構成を更新します。このメソッドは、現在のテストファイルに影響する構成オプションのみをサポートします。
vi.setConfig({
allowOnly: true,
testTimeout: 10_000,
hookTimeout: 10_000,
clearMocks: true,
restoreMocks: true,
fakeTimers: {
now: new Date(2021, 11, 19),
// オブジェクト全体をサポート
},
maxConcurrency: 10,
sequence: {
hooks: 'stack',
// "sequence.hooks" のみをサポート
},
});
vi.resetConfig
- 型:
RuntimeConfig
以前に vi.setConfig
が呼び出された場合、これにより構成が元の状態にリセットされます。