【Jest】it.eachの活用とit.eachで現在のインデックスを取得する方法について

# はじめに
ボタンの表示テストを作成する際、ボタンテキストとclassが違うだけの類似テストをコピペしていたのですが、もっと良い方法ないかなと調査した結果、現状使用している`it.each`についての備忘録です。
# 環境

```
"@types/jest": "^29.0.3",
"jest": "^29.0.3",
"jest-preset-angular": "^13.0.1",
```

# `test.each(table)(name, fn, timeout)`について

> 次のエイリアスでも使用可能です: `it.each(table)(name, fn)` と ``it.each`table`(name, fn)``
異なるテストデータで同じ内容のテストスイートを行う場合は、`test.each`を使用します。 `test.each`によりテストを1回だけ記述し、データを渡すことができます。

※[jestjs.io](https://jestjs.io/ja/docs/api#testeachtablename-fn-timeout)からの引用です。

# 網羅したいテスト・まとめたいテストは`it.each`を使用する。
異なるデータで同じ内容のテストを行う場合など`it.each`でまとめると確認がしやすくなります。
次の例はテスト数が2件なのでメリットを感じづらいですが、`it.each`を使用することで、同じ内容のテストであることが分かりやすく、新たにボタンを追加する場合の行数も減らせるので可読性が上がりそうです。
## `it.each`を使用せず、itで書いたボタンの表示テスト
```sample.spec.ts
it("新しい予約ボタンが表示されているか", () => {
  let button = fixture.nativeElement.querySelector(".create-reserv-button");
  expect(button).toBeTruthy();
  expect(button.textContent).toContain("新しい予約ボタン");
});
it("検索ボタンが表示されているか", () => {
  let button = fixture.nativeElement.querySelector(".search-button");
  expect(button).toBeTruthy();
  expect(button.textContent).toContain("検索ボタン");
});
```
## `it.each`を使用してまとめたボタンの表示テスト
```sample.spec.ts
describe("ボタン表示のテスト", () => {
  it.each([
    ["新しい予約ボタン", ".create-reserv-button"],
    ["検索ボタン", ".search-button"],
  ])("%sボタンが表示されているか", (labelTxt, btnSelector) => {
    let button = fixture.nativeElement.querySelector(btnSelector);
    expect(button).toBeTruthy();
    expect(button.textContent).toContain(labelTxt);
  });
});
```

describeでまとめると、下記のように結果が出力されて確認もしやすくなりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985483/f25b45f0-d9bd-1f02-0e12-1c659f407022.png)


# `it.each`で現在のインデックスを取得する方法
ngForで表示データ配列のインデックスをclass名に使用しているパターンがあり、必要になりました。

テストのタイトルにインデックスを追加し、タイトルから取得する方法で実現しました。以下手順です。

## 対応内容
`it.each`の`name`テストブロックのタイトルの末尾に`:%#`を追加する。※`:%#`はテストケースインデックスのパラメータ。

```
it.each(["test", ".test-wrapper"])("%sの要素が表示されているか :%#",
```


`expect.getState().currentTestName`：現在のテストタイトルを取得する。
`testName.lastIndexOf(":")+1`：タイトルの末尾`:`以降をインデックスとして取得する。
```
it.each(["test", ".test-wrapper"])("%sの要素が表示されているか :%#",(lblText, selector) => {
  const testName = expect.getState().currentTestName;
  const testIndexPos = testName.lastIndexOf(":")+1;
  const currentIdx = parseInt( testName.substring(testIndexPos ));
  let element = fixture.nativeElement.querySelector(`${selector}_${currentIdx}`) as HTMLDivElement;
  expect(element).toBeTruthy();
});
```

上記でインデックスは取得できました。
自分はテストファイルを跨いで共通使用できるように、関数化・ヘルパーファイルを作成して追加しました。

```
_global.getCurrentTestIndex = function () {
    const testName = _global.expect.getState().currentTestName;
    const testIndexPos = testName.lastIndexOf(":") + 1;
    const currentTestIndex = parseInt(testName.substring(testIndexPos))
    return currentTestIndex;
};
```
共通処理について：[【Jest】テストファイルを跨いで共通使用する関数を管理する方法について #TypeScript - Qiita](https://qiita.com/matcha_kinako/items/49236eace5e6e896744d)


# `table`を連想配列の配列で受け渡すこともできる。

`test.each(table)(name, fn, timeout)`の`table`はfnに引数として渡される配列の配列です。
これを連想配列で下記のように受け渡すことも可能です。

```
it.each([
    {
        testTitle: "テスト要素１",
        selector: ".test-elem1",
        val: "テスト１"
    },
    {
        testTitle: "テスト要素２",
        selector: ".test-elem2",
        val: "テスト２"
    },
])("$testTitleが表示されているか", fakeAsync(({ testTitle, selector, val }) => {
    let elem = fixture.nativeElement.querySelector(selector) as HTMLDivElement;
    expect(elem).toBeTruthy();
    expect(elem.textContent).toBe(val);
}));
```

テストタイトルは「テスト要素１が表示されているか」のように出力されます。


# 参考
- [Jest](https://jestjs.io/ja/docs/api#testeachtablename-fn-timeout)
- [自動テストを見やすくするためのテクニック #JavaScript - Qiita](https://qiita.com/waterada/items/08632a5cb6b897b09aa1)
- [javascript - How to get current test index in jest - Stack Overflow](https://stackoverflow.com/a/75313857)