WIP Jestを使ったテストまとめ

### 基本メソッド
```JavaScript
// toBe
it("数字のテスト", () => {
  expect(1 + 2).toBe(3);
});

it("文字列のテスト", () => {
  expect("abc").toBe("abc");
});

it("真偽値のテスト", () => {
  expect(true).toBe(true);
});

// toEqual
it("配列のテスト", () => {
  const arr1 = [1, 2, 3];
  const arr2 = [1, 2, 3];
  expect(arr1).toEqual(arr2);
});

it("オブジェクトのテスト", () => {
  const obj1 = { a: 1, b: 2 };
  const obj2 = { a: 1, b: 2 };
  expect(obj1).toEqual(obj2);
});

// not
it("2+2は5ではない", () => {
  expect(2 + 2).not.toBe(5);
});

/*
  toThrow -> 期待通りに例外処理が発生するかどうかをテストする
  例:割り算の関数を作った場合を想定して、割る数の値が0だったら例外処理としてエラーを返す場合
*/
// divide.ts -> 割り算の関数
export function divide(a: number, b: number): number {
  if (b === 0) {
    throw new Error("divide by zero");
  }
  return a / b;
}

// divide.test.ts -> 割り算のテスト
import { divide } from "./divide";

it("0で割るとエラーになる", () => {
  expect(() => divide(1, 0)).toThrow();
});
```

### 事前・テスト後に処理を走らせて何かしらの設定をしたいケース
- `beforeEach` : 各テスト（it）の前に実行される
- `afterEach` : 各テスト（it）の後に実行される
- `beforeAll` : グループ内のすべてのテストの前に1度だけ実行される
- `afterAll` : グループ内のすべてのテストの後に1度だけ実行される
```JavaScript
// ↓は例
describe("コンポーネントテスト", () => {
  beforeAll(() => {
    // テストを実行する前にセットしておくべき処理を記述しておく
  });

  describe("ボタンテスト", () => {
    afterEach(() => {
      // ボタンテスト内の処理が終わったら設定をクリアするような処理を記述する
    });

    it("ボタンが押された時の挙動", () => {
      // テストの処理内容を記述
    });
  });
});
```

### 処理を何パターンか登録したいケース
- it.eachメソッドを使う
- 想定したいケースはテンプレートリテラル内に記述する
- 変数名や結果などの値は | で区切って定義する
```JavaScript:sum.ts
export function sum(a: number, b: number): number {
  return a + b;
}
```

```JavaScript:sum.test.ts
import { sum } from "./sum";

it.each`
  a     | b     | expected
  ${1}  | ${2}  | ${3}
  ${1}  | ${-2} | ${-1}
  ${10} | ${20} | ${30}
`("sum($a, $b)は$expectedを返す", ({ a, b, expected }) => {
  expect(sum(a, b)).toBe(expected);
});
```

### 非同期関数のテスト
- 非同期関数のテストでは `async/await` で処理を記述する
- 成功時のテストと失敗時のテストとで処理を分けて記述する
- 失敗パターンでのテストでは `trycatch` を使って **try内に失敗が起きる処理** を記述して  **catch内でテストケースを記述** する
```JavaScript:delay.ts
export function delay(message: string, time: number): Promise<string> {
  return new Promise((resolve, reject) => {
    if (time >= 0) {
      return setTimeout(() => resolve(message), time);
    } else {
      reject(new Error("0秒以上の時間を指定してください"));
    }
  });
}
```

```JavaScript:delay.test.ts
import { delay } from "./async";

it("成功パターン:指定された時間後にメッセージを返す", async () => {
  const result = await delay("hello", 1000);
  expect(result).toBe("hello");
});

it("失敗パターン:timeがマイナスの場合はエラーになる", async () => {
  try {
    const result = await delay("hello", -1000);
  } catch (e: any) {
    expect(e.message).toBe("0秒以上の時間を指定してください");
  }
});
```

### モック関数のメモ（後で編集する）
わざわざモック関数にしてテストする意味が分からない
外部ファイルから関数をimportしてテストするのと何が違うのかが分からない
```JavaScript
it("モック関数の作成方法", () => {
  const mockFn = jest.fn();
  // mockImplementationの引数に渡した関数がモック関数の動作として実行される
  mockFn.mockImplementation(() => "hello");
  expect(mockFn()).toBe("hello");
});

it("モック関数の戻り値を設定", () => {
  const mockFn = jest.fn();
  // mockReturnValueの引数にはモック関数の戻り値を設定できる
  mockFn.mockReturnValue("hello");
  expect(mockFn()).toBe("hello");
});

it("モック関数に非同期な戻り値を設定", async () => {
  const mockFn = jest.fn();
  // mockResolvedValueの引数にはPromiseオブジェクトを設定できる
  mockFn.mockResolvedValue("hello");
  // async/awaitを使って非同期処理を実行
  const result = await mockFn();
  expect(result).toBe("hello");
});

it("モック関数が呼び出されたかどうかを検証", () => {
  const mockFn = jest.fn();
  // mockFnが呼び出されたかどうかを検証 ※重要:expectの引数には関数自体を渡すためmockFn()のように()をつけない
  expect(mockFn).not.toHaveBeenCalled();
  // mockFnを実行して、toHaveBeenCalledがtrueになることを検証
  mockFn();
  expect(mockFn).toHaveBeenCalled();
});

it("モック関数が複数回呼び出されたかどうかを検証", () => {
  const mockFn = jest.fn();
  // mockFnが2回呼び出されたかどうかを検証
  expect(mockFn).not.toHaveBeenCalledTimes(2);
  // mockFnを2回実行して、toHaveBeenCalledTimesがtrueになることを検証
  mockFn();
  mockFn();
  expect(mockFn).toHaveBeenCalledTimes(2);
});

it("モック関数が指定した引数で呼び出されたかどうかを検証", () => {
  const mockFn = jest.fn();
  // mockFnが引数"hello"で呼び出されたかどうかを検証
  expect(mockFn).not.toHaveBeenCalledWith("hello");
  // mockFnを引数"hello"で実行して、toHaveBeenCalledWithがtrueになることを検証
  mockFn("hello");
  expect(mockFn).toHaveBeenCalledWith("hello");
});
```