1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【備忘録】Jestの「モック関数」って、何をしているの?

Posted at

はじめに

これまでReact学習の一環としていくつかのアプリ制作を行ってきて、その際にJestを使ったテストを実行することがありました。

Jestのテストで必要不可欠となるモック関数のしくみと仮データの渡し方について、自分なりに気づいたことを備忘録代わりとして記事にしようと思います。

この記事は学習のアウトプットを目的としたものなため一部不正確な記述がある可能性がございます。予めご了承ください。

そもそも「モック」(「モックアップ」)とは?

モック関数について考える前提として、そもそも「モック」とは何かを見てみます。
モック(モックアップ)の意味は以下のようなものがあります。

モックアップとは、実物とほぼ同様に似せて作られた模型のことである。
引用元:https://ejje.weblio.jp/content/mock+up#google_vignette

商品化する前に作った模型やレプリカ、本物ではないもののことを指します。
引用元:https://news.mynavi.jp/article/20210611-1892998/

上記の記述から、モック関数は関数版のモック(モックアップ)、つまり実際の関数と似せて作られているが本物の関数ではないものとなります。

その前提を踏まえて実際のモック関数の記述を見てみましょう。

jest.fn()について

jest.fn()は、先ほどの例を踏まえると特定の関数のレプリカを作るための記述です。
jest.fn()を使った関数は**「関数のレプリカ」**です。事前に関数の処理が記載されていた関数名であっても、モック化した関数にはその関数の処理は実行されません。

使い方としては以下のような形です。ここではsampleFunctionをモック関数として登録しています。

sample.jsx
const sampleFunction = jest.fn()

jest.mockについて

jest.mock他のモジュールの関数をモック化する場合に使います。

sample2.jsx
import { hoge } from "./sample"

jest.mock("./sample"), ()=>{
    return hoge; jest.fn()
}

上記の例で言うとインポートしてきたhoge関数をモック化しています。

インポートした関数(hoge)をモック化することで、関数hogeのインポート元で定義されていた処理は該当のファイル内で実行されなくなります。
裏を返すと、該当の関数(hoge)は「元々の処理は実行されないものの関数としては存在している」状態です。

これがどういった意味を持つのかは次の章で見ていきます。

モック関数の活用例

モック関数を活用する場面の例としてデータベース連携の絡むテストコードが挙げられます。

もしDB連携用の関数をそのままテストコードで使うと、テストコード内で実際のDB内の値を参照したりDBに値を挿入する処理が発生します。

その場合、DB内のデータに依存したテストとなるためテストの汎用性が下がります。また、挿入関数をそのまま使う場合、テストを行うたびにデータがDB内に挿入されてしまいます。


具体例を以下に挙げてみます。

下記のようなコードの記述がある場合、App.tsxのコンポーネントをそのままテスト対象にすると、getAllUserSkills関数の処理が実行された際に実際のDBの値を参照することになります。

supabase-function.tsx
export const getAllUserSkills = async () => {
  const response = await supabase.from("skills").select("*");

  if (response.error) {
    throw new Error(response.error.message);
  }

  return response.data;
};
App.tsx
import { getAllUserSkills } from "./utils/supabase-function";

// 上記の関数が使われている場所の処理を抜粋
useEffect(() => {
    const getUserSkill = async () => {
      const gettingSkill = await getAllUserSkills();
        // 以下の処理は略
    };
    getUserSkill();
  }, []);

このままだとDBの値やデータ数が変わるごとにテストの結果が変わりかねません。

そこで活躍するのが先述のモック関数です。
getAllUserSkillsをモック化することで、テストコード内でgetAllUserSkills関数を実行した扱いにでき、DBからの直接データ参照を防げます。

test.spec.tsx
import { getAllUserSkills } from "./utils/supabase-function";

jest.mock("./utils/supabase-function"), ()=> {
    return getAllUserSkills: jest.fn()
}

ただし、この状態ではモック化されたgetAllUserSkills関数の実体がないため、読み込んだ扱いにするデータが存在しません。

問題を解消するためにはモック関数に値を登録する必要があります。
そこで使われるのがmockResolvedValueをはじめとしたMock Functionsです。

Mock Functionとモック関数への値の登録

jest.fn()でモック化した関数にはMock Functionを用いて値を登録したりすることができます。

ここではモック関数に「値を注入して処理が通った扱いにする」ためのMock FunctionであるmockResolvedValueを見てみます。

sample.tsx
test('async test', async () => {
  const asyncMock = jest.fn().mockResolvedValue(43);

  await asyncMock(); // 43
});

引用元:https://archive.jestjs.io/docs/ja/22.x/mock-function-api#mockfnmockreturnvaluevalue

上記コードではモック化された関数asyncMockに対して43という値を注入しています。
ここではasyncMock関数は「43を返す関数」として定義されています。


先ほどのgetAllUserSkills関数のケースも見てみます。

以下のように、先にテストデータとして注入したい値をあらかじめ作成しておくことで直接DBに接続しなくても擬似的なデータフェッチを実現させています。

test.spec.tsx
import { getAllUserSkills } from "./utils/supabase-function";

jest.mock("./utils/supabase-function"), ()=> {
    return getAllUserSkills: jest.fn()
};

describe("登録ページのテスト", () => {
 const RegisterData = {
    user_id: "sample",
    name: "田中太郎",
    description: "よろしく",
  };

test("サンプルテスト", async () => {
 (insertUserData as jest.Mock).mockResolvedValue(RegisterData);

 render(<App />)

 await waitFor(async () => {
  // ここにテストしたい項目が入ります
 });
});

注意点として、 関数のモック化は該当のコンポーネントを呼び出す前に記述してください。
モック関数を先に定義しておかないと、コンポーネント内にある関数(モック化されていないもの)が先に呼び出されてしまい、モック関数の意味をなさなくなるためです。

最後に

ここまで読んでいただきありがとうございます。
モック関数の概念の理解、モック関数を活用したテストコードの作成は初学者にとっては難関だと感じています。実際、私も未だに苦戦しています。

同じようにモック関数に苦しんでいる人の支えになれば幸いです。

参考資料

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?