TestCafeの便利な機能紹介3つ

:::note info
この記事は[CureApp Advent Calendar 2022](https://qiita.com/advent-calendar/2022/cureapp)の13日目です。
:::

弊社では現在、様々な角度からテスト自動化を進めています。担当を決めて自動化を実施、その後QA全体で共有を図っています。私は社内向けのwebサービスのテスト自動化を担当、「[**TestCafe**](https://testcafe.io/)」を使って実装しました。今回はTestCafeで便利だった機能をいくつかご紹介します！

https://testcafe.io/

## 便利な機能その1：Role

**Role**を使うとログイン処理を1箇所にまとめられます。また、管理者・一般ユーザーなど役割が違う場合のユーザー切り替えを簡単に行うことができます。
認証部分をRoleを使って共通化したところ：

```role.js
import { t, Role } from 'testcafe';
const role = Role('http://example.com/login', async t => {
    await t
        .typeText('#login', 'username')
        .typeText('#password', 'password')
        .click('#sign-in');
});
export { role };
```

（詳細は公式ドキュメントを参照ください）

* https://testcafe.io/documentation/402845/guides/intermediate-guides/authentication
* https://testcafe.io/documentation/402836/guides/best-practices/best-practices#use-of-roles-for-login

## 便利な機能その2：Hooks

TestCafeには様々なレイヤーでHookが用意されています。よく使ったのは、**Fixture.beforeEach**です。

TestCafeには**Fixure**というクラスが用意されており、同じURLからスタートするTestをまとめる役割を担います。また、全てのTestは1つのFixtureに属します。このクラスが持つFixture.beforeEachメソッドでは、Fixtureに属する各Testを実行する際、共通の処理を記述できます。ここで先ほど作ったRoleを使うに書けば、各Testでログイン処理を呼び出さなくてよくなるということです。便利！ログイン処理を一度しか書かなくて良いのはやはり気持ちがいい :hugging: 

```example_test.js
import { role } from './role.js';
import { Selector, t } from 'testcafe';

fixture `example`
    .beforeEach(async t => {
        await t.useRole(role)
        await t.navigateTo(`https://devexpress.github.io/testcafe/example/`)
    })

test('H1 in header', async t => {
    await t.expect(Selector(`header`).find(`h1`).innerText).eql(`Example`)
}
```

Hookについては、**ローカル・グローバルなのか、またどのような順で呼ばれるのかしっかり理解しておく**ことが大切です。（Fixture.beforeEachとTest.beforeは同時には呼ばれません。どちらか片方だけ。雰囲気で書いていた箇所があり、沼にはまってしまいました :rolling_eyes::rolling_eyes::rolling_eyes: キチンと理解してテストを書くのは大事！[図は公式より拝借](https://testcafe.io/documentation/403435/guides/intermediate-guides/hooks#hook-order-and-priority)&追記しました。）

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3070374/dc329859-d551-bae3-d243-fc457e46f764.png">


（詳細は公式ドキュメントを参照ください）

* https://testcafe.io/documentation/403435/guides/intermediate-guides/hooks
* https://testcafe.io/documentation/403435/guides/intermediate-guides/hooks#hook-order-and-priority

## 便利な機能その3：Skip

Skipは初期段階で、どのくらいの機能をテストするか、どれくらいのテストケース数になるかを把握するのに多用しました。
テスト自動化の流れを大まかに下記の挙げます：

1. 最初にどれくらいテストケースが必要なのかざっくりと把握するために、まずはFixtureを確定し、枠だけを用意。その際**Fixture.skip**を呼び出しておく。
1. その次に、各FixtureでTestを列挙。そしてやはり枠だけを用意。Fixture.skip同様、**Test.skip**を呼び出しておく。
1. 個々のTestが自動化可能か判断。不可のものは、「:pray::pray::pray:　手動　:pray::pray::pray:」コメントをつけておく。
1. 全てのFixtureに先の処理を実施すると、ざっくりとしたテストケース数を算出することができる。（実装過程で、いくつかテストを追加する必要が出てくるのはある程度想定）
1. 各Test実装時に、skipを外し中身を実装していく。

簡単なテストを書いてみました。まずはFixture全体をskipする例：

```users_test.js
import { role } from './roles.js';    
import { t, Selector } from 'testcafe';
 
fixture.skip `ユーザー`

test('一覧', () => {});   
```

次に、各FixtureでTestを列挙、一部をskip・「:pray::pray::pray:　手動　:pray::pray::pray:」コメントを追加した例：

```producs_test.js
import { role } from './roles.js';
import { t, Selector } from 'testcafe';

fixture `製品`

test('一覧', async t => {
         //empty test ;P                   
});
test.skip('削除', async t => {});
test('🙏🙏🙏手動🙏🙏🙏: 手動で実施するテストだよ！', () => {});
```

テスト出力結果は下記：

```bash
 製品
 ✓ 一覧
 - 削除
 ✓ 🙏🙏🙏手動🙏🙏🙏: 手動で実施するテストだよ！

 ユーザー
 - 一覧


 2 passed (1s)
 2 skipped
```

下のように、それぞれが出力分けされているので、分かりやすい！出力をgrepして手動で実施するテストが何件あるかなど把握できます。また、いくつテストに成功したかも表示されるので進捗管理もできます。
* ✓：パスしたテスト
* -：skipしたテスト
* ✓：🙏🙏🙏手動🙏🙏🙏: 手動で実施のテスト


（詳細は公式ドキュメントを参照ください）

* https://testcafe.io/documentation/403436/guides/intermediate-guides/metadata-and-filtering?search#skip-tests-and-fixtures

## まとめ

RoleやHookを利用することで、効率的に自動化が進められます。またSkip利用で、大まかなテスト自動化の全体像が把握できました。今回FixtureやTestを詰めていく過程で、興味深かったのは、最終的に全てのテストのうち20%が手動で実施する必要があることでした。テスト自動化は便利ですが、やはり手動で残るところはどうしても出てきてしまいます。テスト時間の短縮だけでなく、どこがどれくらいのテストが自動化不可なのかということも把握できることが、テスト自動化のメリットとして挙げられるということが分かりました。