🎃

【小話】Jest.mock()の引数はスコープ外で定義されたものを参照できない

2022/07/29に公開

Jestでテストを書いている時に、mockコピペするのやだなと思い、外部モジュールに切り出して利用しようとして下記のような形で試してみた。

Sample.test.tsx
jest.mock(MockLibs.nextRouter.moduleName, MockLibs.nextRouter.factory)
Mock.ts
import MockOptions = jest.MockOptions;

interface MockVariables<T> {
  moduleName: string;
  factory?: () => T;
  options?: MockOptions;
}

export namespace MockLibs {
  export const nextRouter: MockVariables<any> = {
    moduleName: "next/router",
    factory: () => ({
      useRouter() {
        return {
          route: "/",
          pathname: "/",
          query: {},
          asPath: "/",
          basePath: "/",
          isLocaleDomain: true,
          isReady: true,
          push: jest.fn(),
          prefetch: jest.fn(),
          replace: jest.fn(),
          reload: jest.fn(),
          back: jest.fn(),
          beforePopState: jest.fn(),
          events: {
            on: jest.fn(),
            off: jest.fn(),
            emit: jest.fn(),
          },
          isFallback: false,
          isPreview: false,
          locale: "jp",
        };
      },
    }),
  };
}

下記のエラーが発生

 ● Test suite failed to run

    TypeError: Cannot read properties of undefined (reading 'MockLibs')

なんで?

https://jestjs.io/ja/docs/es6-class-mocks#jestmock-をモジュールファクトリ引数で呼ぶ

注意
jest.mock()の呼び出しはファイルの先頭に引き上げられるため、 Jestはスコープ外の変数へのアクセスを防止します。デフォルトでは、まず変数を定義し、それをファクトリで使用することはできません。Jestはmockという単語で始まる変数に対してはこのチェックを無効にします。しかし、それでも時間通りに初期化されることを保証できるかどうかは、あなた次第です。Temporal Dead Zoneに注意しましょう。

同ファイル内でmockプレフィックスをつけて変数定義すれば、あとは時間通りの初期化さえハンドリングすればなんとかなりそうだったけれど、importの場合はmockプレフィックスつけてもダメだった。

参考

doMockを使えばうまくいくよ的なレスもあったがこれは自分の環境ではダメだった。
https://github.com/facebook/jest/issues/2567#issuecomment-362815736
https://github.com/facebook/jest/issues/2567#issuecomment-361871589

結論

必要なモックは必要な場所で適宜定義することにした。

Discussion

ログインするとコメントできます