はじめに
この投稿は、学習の備忘録として書いています。vi.mockのファクトリから参照したいモック関数などを先に用意するvi.hoistedを整理します。動作を確認したバージョンはVitest 5.0.2です。
1. vi.mockの巻き上げ
vi.mockは、ファイルの先頭へ巻き上げられてから実行されます。そのため、あとに書いた変数をファクトリから参照すると、初期化前の参照になります。
import { expect, it, vi } from "vitest";
const saveMock = vi.fn();
vi.mock("./api", () => ({
save: saveMock,
}));
この並びだと、次のようなエラーになります。
Error: [vitest] There was an error when mocking a module. If you are using "vi.mock" factory, make sure there are no top level variables inside, since this call is hoisted to top of the file.
Caused by: ReferenceError: Cannot access 'saveMock' before initialization
巻き上げられた時点では、const saveMockはまだ実行されていません。
2. vi.hoistedがすること
vi.hoistedに渡した関数は、vi.mockより前に実行されます。そこでvi.fn()を作り、ファクトリから参照します。
import { vi } from "vitest";
const { saveMock } = vi.hoisted(() => ({
saveMock: vi.fn(),
}));
vi.mock("./api", () => ({
save: saveMock,
}));
ファクトリが走る時点では、すでにsaveMockがあります。
3. テスト本体での使い方
作ったsaveMockは、テスト側で操作します。mockResetで呼び出し記録を消し、mockResolvedValueで戻り値を決めます。
import { beforeEach, expect, it, vi } from "vitest";
const { saveMock } = vi.hoisted(() => ({
saveMock: vi.fn(),
}));
vi.mock("./api", () => ({
save: saveMock,
}));
import { save } from "./api";
beforeEach(() => {
saveMock.mockReset();
});
it("returns the resolved value", async () => {
saveMock.mockResolvedValue("ok");
await expect(save("x")).resolves.toBe("ok");
});
import { save } from "./api"が読むsaveは、ファクトリが返したsaveMockです。テスト内のsave("x")は、そのモック関数を呼んでいます。
まとめ
-
vi.mockはファイル先頭に巻き上げられるため、あとに書いた変数をファクトリから参照できません。 -
vi.hoistedに渡した関数はvi.mockより前に実行されるので、そこで作ったモック関数などをファクトリから参照できます。