react-native-testing-libraryをexpoで使ってみる


# インストール
- https://github.com/callstack/react-native-testing-library

### 必要なパッケージをインストール

```
yarn add --dev @testing-library/react-native
yarn add --dev @testing-library/jest-native
yarn add --dev jest-expo
yarn add --dev @testing-library/user-event
```

### package.jsonを編集
```
{
  "scripts": {
    ....
    "test": "jest"
  },
  "jest": {
    "preset": "jest-expo",
    "transformIgnorePatterns": []
  },
```
# react-native-testing-libraryの概要
- 参考：[React Testing Libraryの使い方](https://qiita.com/ossan-engineer/items/4757d7457fafd44d2d2f#findby%E3%82%92%E3%81%84%E3%81%A4%E4%BD%BF%E3%81%86%E3%81%8B)
    - react nativeではないが、基本的な考えやAPI仕様は同じなので役に立つ

### コンポーネント検索のためのAPI
- getByText
    - Textを検索する
    - 該当テキストがない場合、エラーを発生してアサーションを中断する
    - nullであること(toBeNull)をテストしたい場合はqueryを使うこと
- queryByText
    - 該当テキストがない場合nullとなりエラーを発しない
    - それ以外はgetと同じ
- findByText
    - 非同期の処理が入っている場合に使用。結果がでるまでawaitできる
    - react-hook-formライブラリのvalidationを使用する場合は非同期処理のため、これを使わないとエラーが発生する

```javascript
const { findByText } = render(<SignIn />);
expect(await findByText("ユーザーIDを入力してください")).not.toBeNull();
```
    
### Text, Role, TestID
- Text
    - ユーザーが視認できるUI上のテキスト
- Role
    - roleプロパティで各コンポーネントにroleを付与できる
    - buttonやinputなどはroleを付与しなくともデフォルトで使用可能
- TestID
    - testIDプロパティで付与できる

### expect API
- toBeNull()
- not.toBeNull()
    - 否定を表す場合notを使える
- レンダリングされたかの確認
    - expectのみで良い
    
```javascript
        it('UserIDラベルが存在する', () => {
            const { getByText } = render(<SignIn />);
            expect(getByText("ユーザーID"));
        })
```


### 応用
- 非同期に表示されなくなる場合のテスト
    - [公式サイト: Appearance and Disappearance](https://testing-library.com/docs/guide-disappearance/)
    - waitForElementToBeRemovedを使う
    - 以下の例はreact-hook-formを使用しているため、非同期でhelperTextが表示されなくなる場合のテスト例

```javascript
it('半角英数字以外を使用した場合のみ”半角英数字を使用してください”が表示される', async () => {
            const { getByTestId, queryByText, findByText, getByText } = render(<SignIn />);
            const userIdInput = getByTestId("userId")
            fireEvent.changeText(userIdInput, "test#####")
            const loginButton = getByText("ログイン")
            fireEvent.press(loginButton)
            expect(await findByText("半角英数字を使用してください")).not.toBeNull();
            fireEvent.changeText(userIdInput, "testuser")
            await waitForElementToBeRemoved(() => queryByText('半角英数字を使用してください'))  // 非同期になくなるのを確認する場合
        })
```

# TDDでログインフォームを開発する
## まだ未完成です。今後追加していきます。
### test用ファイルを作成
/src/\__test__/Login.spec.jsを作成

```/src/__test__/Login.spec.js
import React from 'react';
import SignIn from '../views/sign/SignIn'
import { render, screen, fireEvent, getByLabelText, queryByRole } from '@testing-library/react-native';
//import userEvent from '@testing-library/user-event';

describe('SignIn Page', () => {

    it('SignInコンポーネントのレンダリング', () => {
        render(<SignIn />);
        //screen.debug()
    })
}
```

# エラー対応
### jestテスト時にエラー発生
```
    Details:

    /Users/ruihirano/MyProjects/FelixPort/react_native_templete/node_modules/@hookform/resolvers/dist/yup.js:2
    import { transformToNestObject } from 'react-hook-form';
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module
```
- yupライブラリでエラーが起きているらしい
- package.jsonにtransformIgnorePatternsを追加することで解決

```
{
  "scripts": {
    ....
    "test": "jest"
  },
  "jest": {
    "preset": "jest-expo",
    "transformIgnorePatterns": []
  },
```