Jestを効率的に実行するためのTips

# 概要

機能が多く初めはなかなか掴み所が難しいJestですが、テスト開発を効率的に行うためのTipsをまとめます。

# Tips

## Watchモードの活用

開発作業中はwatchモードで起動するのが非常に便利なのですが、動きに癖があると感じている方もいるのではないでしょうか？

まずはwatchモードで起動します。

```
npx jest {パターン} --watch
```

`{パターン}`はディレクトリかファイルですが、一部でもOKです。一致するファイルのみテストが実行されます。

テストが完了するとこんな画面になります。

![スクリーンショット 2021-09-15 7.00.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194978/43fa2894-ca94-3e4c-721f-2667243857f4.png)

`w`を押すと詳細が見られますが、下記のコマンドを利用することで条件を変更しながら便利にテストを再実行することができます。

### `Enter`

JestのWatchモードはファイルの変更を自動で検知して再実行してくれますが、
エンターキーを押すことでも同じ条件でテストが再実行されます。
また、実行中にエンターキーを押すと、テストが一時停止されるので条件などを変更して再実行ができます。

### `a`

ファイル名のパターンを指定してテストを走らせた場合、最初の実行はパターンで対象のディレクトリが絞られていますが、`a`を押すとパターンの指定が解除されます。

### `p`

ファイル名のパターンの指定をしなおすことができます。解除したい場合は空文字でエンターを押せばOKです。

### `t`

テスト名のパターンで絞ることができます。解除したい場合は空文字でエンターを押せばOKです。

```typescript
test('ここの文字列の部分一致', () => {...});
```

これらのコマンドの組み合わせで、watchモードにしたまま効率的に開発を続けることができます。

## only, skip

上記のwatchモードコマンド `p`, `t` などで実行するテストを絞ることができましたが、コード上で指定することも可能です。テストを分割する `describe`, `test`などには、`.only`, `.skip`という関数が生えています。

```typescript
describe('テストA', () => {
  test.skip('テストAA', () => {});
});
describe.skip('テストB', () => {...});
```

とすることで、`テストAA`, `テストB`はスキップされます。

また、

```typescript
describe('テストA', () => {
  test.only('テストAA', () => {});
});
describe('テストB', () => {...});
```

で、`テストAA`だけが実行されます。

## describeを活用

テストの階層を作る関数は `describe`と`test(=it)` の２つだけですが、`test`の内容ができるだけ具体的になるように、describeで階層を作成します。

例えば、以下のように書いた場合、

```typescript
const person = {
  age: 20,
  name: {
    kana: 'スズキ',
    kanji: '鈴木',
  }
}

describe('personのテスト', () => {
  test('値が正しいことを確認', () => {
    expect(person.age).toBe(20);
    expect(person.name.kana).toBe('スズキ');
    expect(person.name.kanji).toBe('鈴木');
  })
});
```

![スクリーンショット 2021-09-15 7.14.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194978/cba87759-3f2d-cbaa-c62d-225670fbc732.png)

これでは何をテストしたのか少し曖昧になります。

describeで階層を作成した場合は以下の通りになります。

```typescript
describe('personのテスト', () => {
  describe('値が正しいことを確認', () => {
    test('年齢が20歳', () => expect(person.age).toBe(20));
    
    describe('名前', () => {
      test('カナが「スズキ」', () => expect(person.age).toBe('スズキ'));
      test('漢字が「鈴木」', () => expect(person.age).toBe('鈴木'));
    });
  })
});
```

![スクリーンショット 2021-09-15 7.22.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194978/8b3f2cc5-775c-dab5-62a0-7a633d29e4c4.png)


また、結果を非同期で受け取る場合でも、beforeAllなどを活用することで詳細にtest()を分割することができます。

```typescript
const personApi = () => Promise.resolve({
  age: 20,
  name: {
    kana: 'スズキ',
    kanji: '鈴木',
  }
});

describe('personのテスト', () => {
  let person: any = null;

  beforeAll(async () => {
    person = await personApi();
  });

  describe('値が正しいことを確認', () => {
    // 以下同様 ...
});
```

## verboseフラグ

折角describeをうまく使っても、複数のテストが対象になる場合は詳細な階層を表示してくれません。

![スクリーンショット 2021-09-15 7.26.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194978/4be22544-1579-b4ff-2492-e0a28255722e.png)

詳細が確認したい場合は `--verbose` フラグを設定して実行します。

```bash
npx jest --watch --verbose
```

![スクリーンショット 2021-09-15 7.28.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194978/925f3140-fbcf-9952-bce7-b8e854087a57.png)

## 複数の条件でテストを実行したい

`test.each` という関数が利用できます。

```typescript
const sum = (a: number, b: number) => a + b;

describe('sumの結果が正しい', () => {
  test.each([
    [1, 1, 2],
    [3, 4, 7],
    [1, -1, 0],
    [1, 0, 1],
  ])('%i + %i = %i', (a, b, result) => {
    expect(sum(a, b)).toBe(result);
  })
});
```

![スクリーンショット 2021-09-15 7.53.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194978/cecd1469-b6ec-f46e-ec05-a1e5f5c788b7.png)

## `ts-jest`が遅い

> Jestが遅い場合の理由は色々ありますが、ts-jestの変換が遅い場合の対処です。

ts-jestはTypeScriptで開発をしている場合にjest.config.jsのtransformに設定して、TSファイルをJSに変換するために利用されます。

```typescript
transform: {
  '^.+\\.(ts|tsx)$': 'ts-jest',
},
```

### `esbuild-jest` を利用する

[esbuild-jest](https://github.com/aelbore/esbuild-jest) というツールがありますので、ts-jestの代わりに利用できます。


```bash
npm i -D esbuild-jest
```

```typescript
transform: {
  '^.+\\.(ts|tsx)$': 'esbuild-jest',
},
```

注意として、esbuildがまだ新興ツールなのと、私が利用していて、react周りの変換でエラーが出たりしましたので、バックエンドのコードで利用しています。

### tscでビルドする

自分でｔｓｃで変換してから、.jsファイルでjestを走らせる方法です。これにより、ts-jestによる変換の時間がかからなくなります。

```js:jest.config.js
module.exports = {
  // tscが吐き出すパス配下を参照する様にする
  roots: ['<rootDir>/dist'],
```

注意点として、dist配下はコミットしないと思いますが、このままではsnapshotテストの結果がdist配下に吐き出されてしまいます。
jestによって生成されたsnapshotのみ、もとのｔｓファイルのそばに配置したい場合は [snapshotResolver](https://qiita.com/hedrall/items/61e1809b8acb297193d8) を設定します。