はじめに
この記事では、Vitest というテストフレームワークのモックに利用される vi.fn
、vi.spyOn
、vi.mock
の概要とそれらの使い分けをサンプルつきで記載していきます。
fn
、spyOn
、mock
の使い分け
モック対象によって使い分けます。
-
fn
:関数 -
spyOn
:オブジェクトのメソッド -
mock
:モジュール全体
fn
fn
は、関数をモックします。
以下のサンプルでは、getApples
というモック関数を作成し、その関数が呼び出されることをテストしています。
test("spy function no arguments and no returns", () => {
// Define mock function
const getApples = vi.fn();
// call mock function
getApples();
// check if mock function is called
expect(getApples).toHaveBeenCalled();
});
また、モック関数に引数や戻り値を与えることもできます。
test("spy function returns a product", () => {
const getProduct = vi.fn((product: string) => ({ product }));
getProduct("apples");
expect(getProduct).toHaveReturnedWith({ product: "apples" });
});
さらに モック関数向けに提供されている API を利用して、モック関数のふるまいを操作することもできます。
例えば、以下のサンプルでは、mockReturnValueOnce という API を利用して、モック関数が「5」を返すように操作しています。
test("spy function returns a number", () => {
const getApples = vi.fn(() => 0);
getApples();
expect(getApples).toHaveReturnedWith(0);
getApples.mockReturnValueOnce(5);
getApples();
expect(getApples).toHaveNthReturnedWith(2, 5);
});
spyOn
spyOn
は、引数に指定したオブジェクトのメソッドをモックします。
以下のサンプルでは、cart
というオブジェクトの getApples
メソッドをモックし、モックが一度呼び出されることをテストしています。
const cart = {
getApples: () => 4,
};
test("spy method", () => {
// Define mock method
const spy = vi.spyOn(cart, "getApples");
// call mock method
cart.getApples();
// check if mock is called
expect(spy).toHaveBeenCalled();
expect(spy).toHaveReturnedWith(4);
});
また、mockImplementation
を利用して、メソッドを上書きすることもできます。
const cart = {
getApples: () => 4,
};
test("overwrite spy method", () => {
const spy = vi.spyOn(cart, "getApples").mockImplementation(() => 8);
cart.getApples();
expect(spy).toHaveReturnedWith(8);
});
mock
mock
は、インポートするモジュールを全てモックします。
例えば、以下のように calculate.ts
というモジュールを利用し、props
で渡された値の合計を表示するコンポーネントがあるとします。
export const sum = (num1: number, num2: number): number => {
return num1 + num2;
};
import { FC } from "react";
import { sum } from "../libs/calculate";
type Props = { num1: number; num2: number };
export const Sum: FC<Props> = ({ num1, num2 }) => {
const result = sum(num1, num2);
return <div>The sum is: {result}</div>;
};
このコンポーネントを mock
を利用せず、テストすると以下のようなコードになります。
test("spy module", () => {
render(<Sum num1={1} num2={2} />);
expect(screen.getByText("The sum is: 3")).toBeInTheDocument();
});
mock
を利用すると、以下のように calculate.ts
の結果を上書きすることができます。
test("spy module", () => {
vi.mock("../../libs/calculate", () => {
return {
sum: vitest.fn().mockReturnValue(-10),
};
});
render(<Sum num1={1} num2={2} />);
expect(screen.getByText("The sum is: -10")).toBeInTheDocument();
});
実際のテストでは、外部モジュールをモックしたいときなどに利用することになると思います。