Q. 指定されたパスのすべてのモジュールをモックするmockメソッドは巻き上げられる

Published at
358 words
2min read

インポートされるモジュールを丸ごとモックするようなものなので、巻き上げられる・Hoisting されるのは自然に思えるが、把握していないとハマる可能性がある。

公式を読もう。

Q.#

次のようなコードが上手く動作しないと相談された。

import { describe, expect, vi } from "vitest";
import isValidScope from "./isValidScope";
describe("isValidScope", () => {
test("tokenとしてパースできれば有効", () => {
vi.mock("./parseToken", () => ({
parseToken: () => true,
}));
expect(isValidScope("test")).expect(true);
});
test("tokenとしてパースできなければ不正", () => {
vi.mock("./parseToken", () => ({
parseToken: () => false,
}));
expect(isValidScope("test")).expect(false);
});
});

Why#

すべての vi.mock は巻き上げられ、同一のパスに対して呼ばれていると後に書かれたものが優先されるため、前半のテストが落ちるようになってしまっている。

A.#

次のようにして、mockReturnValue や mockImplementation 経由で差し替えれば良い。

import { describe, expect, vi } from "vitest";
import isValidScope from "./isValidScope";
const mocks = vi.hoisted(() => {
return {
parseToken: vi.fn(),
};
});
vi.mock("./parseToken", () => {
return {
parseToken: mocks.parseToken,
};
});
describe("isValidScope", () => {
test("tokenとしてパースできれば有効", () => {
mocks.parseToken.mockReturnValue(true);
expect(isValidScope("test")).expect(true);
});
test("tokenとしてパースできなければ不正", () => {
mocks.parseToken.mockReturnValue(false);
expect(isValidScope("test")).expect(false);
});
});

vi.hoisted で記述されたものは巻き上げられる。これを使わなければ、vi.mock で使われているメソッドが後から定義されることになるのでエラーが出てしまう。