14
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Vitest のモック関数 fn、spyOn、mock の使い分け

Posted at

はじめに

この記事では、Vitest というテストフレームワークのモックに利用される vi.fnvi.spyOnvi.mock の概要とそれらの使い分けをサンプルつきで記載していきます。

fnspyOnmock の使い分け

モック対象によって使い分けます。

  • fn:関数
  • spyOn:オブジェクトのメソッド
  • mock:モジュール全体

fn

fn は、関数をモックします。

以下のサンプルでは、getApples というモック関数を作成し、その関数が呼び出されることをテストしています。

spyFunctions.test.ts
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();
});

また、モック関数に引数や戻り値を与えることもできます。

spyFunctions.test.ts
test("spy function returns a product", () => {
  const getProduct = vi.fn((product: string) => ({ product }));

  getProduct("apples");

  expect(getProduct).toHaveReturnedWith({ product: "apples" });
});

さらに モック関数向けに提供されている API を利用して、モック関数のふるまいを操作することもできます。
例えば、以下のサンプルでは、mockReturnValueOnce という API を利用して、モック関数が「5」を返すように操作しています。

spyFunctions.test.ts
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 メソッドをモックし、モックが一度呼び出されることをテストしています。

spyFunctions.test.ts
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 を利用して、メソッドを上書きすることもできます。

spyFunctions.test.ts
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 で渡された値の合計を表示するコンポーネントがあるとします。

calculate.ts
export const sum = (num1: number, num2: number): number => {
  return num1 + num2;
};
Sum.tsx
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 を利用せず、テストすると以下のようなコードになります。

Sum.test.tsx
test("spy module", () => {
  render(<Sum num1={1} num2={2} />);
  expect(screen.getByText("The sum is: 3")).toBeInTheDocument();
});

mock を利用すると、以下のように calculate.ts の結果を上書きすることができます。

Sum.test.tsx
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();
});

実際のテストでは、外部モジュールをモックしたいときなどに利用することになると思います。

参考

14
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?