Jest 入門1

## はじめに

こちらは新米エンジニアがJestのキャッチアップをするためのまとめノートです。
本編では、基本設定から簡単なテスト例(同期コード)の作成、非同期コードのテスト、mockとspyまで扱います。

## 基本設定

まずは、Jestをインストールします。

```bash
npm install --save-dev jest
```

次に、package.jsonファイルにテストスクリプトを追加します。これにより、npm testコマンドでJestを実行できるようになります。

```json
"scripts": {
  "test": "jest"
}
```

以上の設定で、*.test.jsや*.spec.jsといった命名規則に従ったテストファイルを自動的に認識して実行します。

## 基本的なテスト例

### テストスイート

テストスイートは、関連する複数のテストケースをグループ化するために使用されます。
Jestでは、describeブロックを使用してテストスイートを作成します。

```javascript
describe('Calculator functions', () => {
  // テストケースを並べていく
});
```

上記の例では、「Calculator functions」というテストスイートが作成され、この中に複数のテストケースを含めることができます。

### テストケース

テストケースは、特定の条件下でのコードの挙動を検証するためのものです。Jestでは、test関数(or)it関数を使用して個々のテストケースを定義します。

```javascript
describe('Calculator functions', () => {
  test('adds 1 + 2 to equal 3', () => {
    expect(sum(1, 2)).toBe(3);
  });

  it('subtracts 5 - 2 to equal 3', () => {
    expect(subtract(5, 2)).toBe(3);
  });
});
```

ここでは、「adds 1 + 2 to equal 3」と「subtracts 5 - 2 to equal 3」という二つのテストケースがあり、それぞれ加算と減算の機能をテストしています。
これらのテストスイートとテストケースを使用することで、コードの特定の部分が期待通りに動作することを確認します。

### アサーション

アサーションは、テスト中に特定の条件が真であることを確認するために使用されるステートメントです。
下記３点がキーポイントです。

1. 期待値:
アサーションは、特定のコードが実行された後の期待値を定義します。この期待値は、関数の戻り値、変数の状態、またはその他の実行結果など、様々なものに対して設定することができます。

2. アサーションの実行:
実行されたコードの結果が期待値と一致するかどうかを検証します。一致すれば、アサーションはパス（成功）と評価されます。一致しなければ、アサーションは失敗し、テストは失敗したと見なされます。

3. アサーションメソッド:
Jestではexpectメソッドを使用してアサーションを作成します。expectメソッドはテストしたい値を引数として受け取り、期待値を引数にとるtoBeをチェインさせて検証します。
以下の例では、加算の結果が4であることを検証しています。

```javascript
test('two plus two is four', () => {
  expect(2 + 2).toBe(4);
});
```

## 非同期テスト

非同期テストは、データベースへのアクセスやHTTPリクエスト等での非同期操作を含むコードの動作を検証する際に行います。

### promiseのテスト

Jestでpromiseベースの操作をテストするには、単純にpromiseを返すテスト関数を記述します。

```javascript
test('the data is peanut butter', () => {
  return fetchData().then(data => {
    expect(data).toBe('peanut butter');
  });
});
```

上記のテストでは、fetchData関数がpromiseを返し、その解決された値が特定の期待値（この場合は「peanut butter」）と一致することを検証しています。
その間、Jestはpromiseが解決されるまでテストの完了を待機します。

### async/await

asyncキーワードをテスト関数に追加し、awaitを使用して非同期関数の結果を待ちます。

```javascript
test('the data is peanut butter', async () => {
  const data = await fetchData();
  expect(data).toBe('peanut butter');
});
```

### doneコールバックの使用

場合によっては、複数の非同期動作が関与するテストでdoneコールバックを使用することがあります。

```javascript
test('the data is peanut butter', done => {
  fetchData().then(data => {
    expect(data).toBe('peanut butter');
    done();
  });
});
```

この例では、doneコールバックをテスト関数に渡し、非同期操作が完了した後に呼び出しています。
これにより、Jestは非同期操作が完全に終了するまでテストの完了を待ちます。
doneコールバックを使用する際に呼び出しがないと、テストがタイムアウトになる可能性があるので注意が必要です。

## mockとspy

mockとspyは外部依存関係や複雑な関数のテストを行う際に有用です。

### mock

外部モジュールや関数を模倣（mock）するための機能です。
mockを使用すると、テスト中に特定の動作をシミュレートしたり、外部システムとの実際の通信を避けることができます。
例えば、API呼び出しを行う関数をテストする場合、実際のAPIを呼び出す代わりに、mockで応答を模倣することができます。

```javascript
jest.mock('apiModule', () => ({
  fetchData: () => Promise.resolve('peanut butter')
}));

test('the data is peanut butter', async () => {
  const data = await fetchData();
  expect(data).toBe('peanut butter');
});
```

上記の例では、apiModuleのfetchData関数を模倣しており、実際のAPI呼び出しを行わずにpromiseを解決しています。

### spy
mockは新しい実装や戻り値をカスタムした検証ができる一方で、spyは既存の関数の動作を変更せずにそのままの呼び出しを追跡します。
これは、関数がどのように、いつ、どのような引数で呼び出されたかを確認するのに役立ちます。

下記のように.spyOnメソッドを使用してspyを作成することができます。

```javascript
const video = {
  play() {
    return true;
  }
};

test('plays video', () => {
  const spy = jest.spyOn(video, 'play');
  const isPlaying = video.play();

  expect(spy).toHaveBeenCalled();
  expect(isPlaying).toBe(true);

  spy.mockRestore();
});
```

この例では、video.playメソッドにspyを設置しています。
テストでは、playメソッドが呼び出されたかどうかをtoHaveBeenCalledマッチャーを使って確認しています。
また、mockRestoreメソッドは、spyを初期状態に戻すことで他のテストでのspy仕様時に影響が出ない様にするためのものです。

mockとspyは、テストをより制御しやすくするための重要なツールです。これらを利用することで、外部依存性の影響を受けずにコードの特定の側面を隔離してテストすることが可能になります。また、関数の呼び出し回数や呼び出し時の引数など、詳細な情報を取得することもできます。

## まとめ
間違えているところや足りないところ等があれば是非ともご指摘いただけるととても嬉しいです。

## 参考文献

- [公式ドキュメント](https://jestjs.io/docs/en/getting-started)