【Jest】なぜモック化してテストする必要があるのか

# はじめに
今回はsupabaseなどから外部の関数を用いる際になぜモック化してテストした方が良いのかについて簡単にまとめていきます。簡単なreactやテストの知識があった方がいいかもです。


# そもそもモックとは
モックとはテストに必要な部品(オブジェクトや関数)の値を擬似的に設定するものです。これにより実際のオブジェクトや関数の代わりに使用され、テストの際に特定の挙動をシミュレートすることができます。

# モックなしで書く場合
例えばReactで以下のようにTODO(タイトル,時間,削除ボタン)を表示しているコードがあるとします。~~実際は学習記録アプリですが...~~
```jsx
/*---App.jsx(一部)---*/
<ul data-testid="todo-list">
    {records.map((record, index) => (
        <li key={index} className="list-style">
          <p>{record.title}</p>: {record.time}時間
          <button onClick={() => onClickDeleteRecord(record.id)}>
            削除
          </button>
        </li>
    ))}
</ul>
```
この時TODOが3つ表示されていることをテストしたい場合以下のようになります。
```js
/*---sample.spec.jsx---*/
import App from "../App";
import React from "react";
import { render, screen, fireEvent, waitFor} from "@testing-library/react";


test("TODOが3つ表示されること", async () => {
    render(<App />);
    await waitFor(() => screen.getAllByTestId("todo-list"));
    const records = screen.getAllByTestId("todo-list")[0].querySelectorAll("li");
    expect(records.length).toBe(3);
  });
```
モック化せずテストを書く場合、実際のDBのデータ(今回はsupabaseのデータ)に依存してしまうため不安定なテストになってしまいます。例えば現状のsupabaseのテーブルにデータが3個登録されていれば今回のテストは通りますが、ユーザーがデータを追加したり削除してデータの数が変わってしまう場合このテストは通らなくなってしまいます。
# モックありで書く場合
なので次は実際のDBに依存しない仮のモックデータを使ってテストをします。これにより実データに依存しない安定したテストを実施することができます。
```js
/*---sample.spec.jsx---*/
import App from "../App";
import React from "react";
import { render, screen, fireEvent, waitFor} from "@testing-library/react";

// モックデータ
const mockGetAllRecords = jest.fn().mockResolvedValue([
  { id: 1, title: "title1", time: 3 },
  { id: 2, title: "title2", time: 4 },
  { id: 3, title: "title3", time: 3 }
]);

// もしgetAllRecordsが呼ばれたらmockGetAllRecordsが呼ばれる
jest.mock("../../utils/supabasefunctions", () => {
  return {
    getAllRecords: ()=>mockGetAllRecords(),
}
});

test("TODOが3つ表示されること", async () => {
    render(<App />);
    await waitFor(() => screen.getAllByTestId("todo-list"));
    const records = screen.getAllByTestId("todo-list")[0].querySelectorAll("li");
    expect(records.length).toBe(3);
  });
```
```js
/*---supabasefunctions.js---*/
import {supabase}  from "../utils/supabase"

// study-recordというテーブル全レコードを取得
export const getAllRecords = async () => {
    const records = await supabase.from("study-record").select("*");
    return records.data;
}
```
これで何となくモックを使う理由がわかったかと思います。
<details>
  <summary>GPTにもちょっと聞いてみた</summary>
  モック化してテストを行う理由はいくつかあります。
  
  1. **依存関係の管理** モックを使用することで、テスト対象のコードが依存する外部リソースやライブラリの振る舞いを制御できます。例えば、API呼び出しや外部サービスの使用などが該当します。
  2. **独立したテスト** モックを使用することで、テストが外部環境に依存せず、独立して実行できます。これにより、テストの信頼性と安定性が向上します。
  3. **再現性の確保** モックを使って特定のシナリオやエラー条件を再現することができます。例えば、ネットワークエラーのシミュレーションや特定のデータの返却などが該当します。
  4. **高速なテスト実行** モックを使用することで、外部リソースへのアクセスが不要になり、テストが高速に実行できる場合があります。特に、テストが頻繁に実行される場合や大規模なデータやリソースが関与する場合に有効です。
  5. **コードの分離** モック化することで、テストコードと実際の実装コードを分離し、テストの管理やメンテナンスを容易にします。特に、外部リソースにアクセスするコードをテストする場合に役立ちます。

  モック化は、特にユニットテストや結合テストのコンテキストで一般的に使用され、コードの品質向上やバグの早期発見に貢献します。
</details>

# まとめ
```
- モックとはテストに必要な部品(オブジェクトや関数)の値を擬似的に設定するもの
- モックを使うことで実データに依存しない安定したテストができる
```
# おわりに
はじめはそもそもモックが何なのかもよくわかってなかったですが、だんだんイメージがついてきました。今後は要素の削除や追加の関数もモック化していきます。

# JISOUのメンバー募集中
プログラミングコーチングJISOUではメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか？

気になる方はぜひHPからライン登録お願いします:point_down:

https://projisou.jp