Jest & React Testing Libraryに入門してみた

# はじめに
Jest React Testing Libraryについて理解を深めるため記事にしました。

# 理解するためのアプローチ
以下の流れで進めることにしました。

1. JestとReact Testing Library がそもそも何なのかを理解する
2. JestとReact Testing Libraryの基本的なテストコードの書き方を理解する
3. 自分で作ったプロジェクトにテストを組み込んで見る


# 1. Jest & React Testing Library の基本を知る
Jest と Testing Library が何をするものなのか、大まかに理解する。

## Jestとは
JavaScript テスティングフレームワーク
関数やロジックを対象としてテストすると理解

> Jest はあらゆる JavaScript のコードベースの正しさを保証するために設計された JavaScript テスティングフレームワークです。親しみやすく、豊富な機能を持つAPIによって簡単にテストを書くことができ、さらには素早く結果を得ることができます。

https://jestjs.io/ja/

## React Testing Libraryとは
Reactコンポーネントのテストを行うためのライブラリ。
「ユーザーの視点でテストを書く」 ためのツールを提供

ユーザーがボタンを押したり、画面に入力した際正しく動くかどうかなどユーザーが行う操作に対してテストすると理解

https://testing-library.com/


# 2. 基本的なテストの書き方を覚える
## Jest

公式のチュートリアルでは以下の順番で説明されていました。
1. Matcher
2. 非同期コードのテスト
3. セットアップと破棄
3. モック関数
3. Jestプラットフォーム

https://jestjs.io/ja/docs/getting-started

### 1.Matcher

```javascript:sum.js
function sum(a, b) {
  return a + b;
}
module.exports = sum;
```

```javascript:sum.test.js
const sum = require('./sum');

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

expect()にテストしたい対象を指定（sum関数）
toBe()に期待値を指定

値が厳密に等しいかどうかをテストしてくれる。

### 2.非同期コードのテスト
・ Async/Await
・ .resolves / .rejects

> async と awaitをテストで使用できます。 非同期テストを書くには、 testに渡す関数の前にasync キーワードを記述するだけです。 例えば、同じfetchData シナリオは次のようにテストできます:

テストコードでもasync/awaitの使用ができると理解

>  expect宣言で.resolves マッチャを使うこともでき、Jestはそのpromiseが解決されるまで待機します。 promiseがrejectされた場合は、テストは自動的に失敗します。

> promiseがrejectされることを期待するケースでは.rejects マッチャを使用してください。 .resolvesマッチャと似た動作をします。 promiseが成功した場合は、テストは自動的に失敗します。

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

test('the fetch fails with an error', async () => {
  await expect(fetchData()).rejects.toMatch('error');
});
```

テスト対象expect（fetchData()）の処理が完了されることを確認できるのが.resolvesマッチャ
テスト対象expect（fetchData()）の処理が完了されないことを確認できるのが.rejectsマッチャ

### 3.セットアップと破棄
#### 繰り返しのセットアップ　beforeEachとafterEach

> 多くのテストで繰り返し行う必要がある場合は、beforeEach と afterEach フックを使用します。

各テストで同じ処理を何度も実行する必要がある場合に使用できると理解

``` javascript
beforeEach(() => {
  initializeCityDatabase();
});

afterEach(() => {
  clearCityDatabase();
});

test('city database has Vienna', () => {
  expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan', () => {
  expect(isCity('San Juan')).toBeTruthy();
});
```

city database has Vienna、city database has San Juanそれぞれのテスト実行前にinitializeCityDatabase();を実行
テスト実行後はclearCityDatabase();が実行される

### 4.モック関数

const mock = jest.fn();でモックを作れる。
テスト対象で実際に動かしたくないときに使用。


### 5.Jestプラットフォーム
Jestの特定の機能をパッケージ化して便利にしたもの


## React Testing Library

https://qiita.com/ossan-engineer/items/4757d7457fafd44d2d2f



https://testing-library.com/docs/react-testing-library/example-intro

こちらの記事とドキュメントを参考にしました。

### 1.レンダリング

render関数でテストしたいコンポーネントをレンダリング

```javascript
import App from "../App";
import React from "react";
import { render, screen } from "@testing-library/react";

describe("Title Test", () => {
  it("test1", () => {
    render(<App />);
		screen.debug();
  });
});
```
screen.debug()でコンポーネントの中身をコンソール上で表示できる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3993509/b6b19567-e46a-4730-938e-ecab0a705a87.png)

### 2.要素の取得

レンダリングした要素を取得して、テスト対象を特定します。
要素の取得方法は下記の記事を参考にしました。

https://qiita.com/ossan-engineer/items/4757d7457fafd44d2d2f

getByとfindByの違いについて、エラーを通して理解しました

   - getBy*: 要素が見つからないと例外を投げる（存在確認用）
   - queryBy*: 要素が見つからなくてもnullを返す（非存在確認用）
   - findBy*: 非同期で要素を探し、Promiseを返す（非同期表示確認用）

https://qiita.com/asa129/items/daf4c12cc5571a5a4b17


# 3.自分で作ったプロジェクトにテストを組み込んで見る

## テストしたいこと
入力をしないで登録を押すとエラーが表示される

## 実現するために
`expect`：登録ボタンを押下
`Matcher`：画面に表示されたエラー内容が期待値通りになること
で実現できるか考えてみました


```javascript
import App from "../App";
import React from "react";
import '@testing-library/jest-dom'
import { render, screen, within, waitFor, cleanup} from "@testing-library/react";
import userEvent from "@testing-library/user-event";

afterEach(() => {
	cleanup();
});

describe("RegistDelete Test", () => {
	it("入力をしないで登録を押すとエラーが表示される", async () => {

		render(<App />);

		// 登録ボタン押下
		userEvent.click(await screen.findByTestId("add"));

		expect(await screen.findByTestId("error")).toHaveTextContent("入力されていない項目があります");
	});
});
```


# おわりに
基本的なことをまとめてみました。モック関数などまだまだわからないことばかりなので、今後も学習を進めて適切なテストコードをかけるようになりたいです。

# 参考
https://jestjs.io/ja/

https://testing-library.com/

https://qiita.com/ossan-engineer/items/4757d7457fafd44d2d2f