Vitestでクラスをmockする

2024/11/04に公開

前提条件

まず、vite.config.tsglobals: trueに設定していることを前提としています。これは、テストでグローバルにviオブジェクトを使用するための設定です。

Vitestでクラスをモックしたかったのですが、Jestと同様の方法が使えなくて困っていました。

今回はGoogleカレンダーのAPIライブラリをサンプルとして、JestとVitestでの違いを見ていきます。

Jestでのクラスのモック

Jestでは、以下のようにクラスをモックすることができます。

import { calendar_v3 } from "@googleapis/calendar";

jest.mock('@googleapis/calendar');

test("example jest mock", async () => {
    const mockCalendar = {
      events: {
        list: jest.fn(),
      },
    } as unknown as jest.Mocked<calendar_v3.Calendar>;
    
    (calendar_v3.Calendar as jest.Mock).mockImplementation(() => mockCalendar);
    
    (mockCalendar.events.list as jest.Mock).mockImplementation(() => ({
      data: {
        items: [
          {
            start: { dateTime: '2023-05-01T10:00:00+09:00' },
            end: { dateTime: '2023-05-01T11:00:00+09:00' },
          },
        ]
      }
    }));
    
    const calendar = new calendar_v3.Calendar({});
    const result = await calendar.events.list();
    
    expect(mockCalendar.events.list).toHaveBeenCalledTimes(1);
});

mockCalendarbeforeEachに配置して、(mockCalendar.events.list as jest.Mock).mockImplementationをtest内で変更する、というような使い方をしてました。

Vitestでの解決策

Vitestでは、Jestと同じコードがそのまま使えないため、少し異なるモックの方法を用います。
以下のように、vi.mockedCalendarクラスをモックし、mockImplementationevents.listのオブジェクトを返すようにしています。

import { calendar_v3 } from "@googleapis/calendar";
import { MockedObject } from "vitest";

vi.mock('@googleapis/calendar');
test("example vi mock", () => {
    const mockCalendar = {
      events: {
        list: vi.fn().mockResolvedValue({
          data: {
            items: [
              {
                start: { dateTime: '2023-05-01T10:00:00+09:00' },
                end: { dateTime: '2023-05-01T11:00:00+09:00' },
              },
            ]
          }
        }),
      },
    } as unknown as MockedObject<calendar_v3.Calendar>;
    vi.mocked(calendar_v3.Calendar).mockImplementation(() => mockCalendar);

    const calendar = new calendar_v3.Calendar({});
    const result = await calendar.events.list();
    
    expect(mockCalendar.events.list).toHaveBeenCalledTimes(1);
});

試したこと

  • jestをviに置き換えたがvi.mockの型アサーションが効かない
(calendar_v3.Calendar as typeof vi.mock).mockImplementation(() => mockCalendar);
});
  • 公式ドキュメントにあるようにvi.mocked()で囲む
vi.mocked(calendar_v3.Calendar.prototype.events.list).mockImplementation(() => mockCalendar);

class.prototypevi.mockedを試みましたが、Cannot read properties of undefined (reading 'list')エラーが発生します。

Discussion