TestCafe で E2E テストを始めよう #1 - 概要説明 と Hello World

TestCafe とは?

TestCafe とは、Developer Express Inc. というアメリカのシステム開発会社が開発している E2E テストツールです。

同社はもともと TestCafe Studio という IDE(E2E テストケースオーサリングツール)を商用として提供していましたが、2016 年に TestCafe Studio のコア部分を OSS として提供するようになりました。今回ご紹介するのは、このコア部分についてです。

シリーズ一覧

大まかな特徴

TypeScript サポート

TypeScript で記述したテストコードをそのまま実行できます。これによりテストケースが増大したとしても保守性を損なうことなく見通しの良いコードでの運用が期待できます。もちろん普通の JavaScript もサポートしています。

非 WebDriver 依存

一昔前の E2E テストツールといえば、各種ブラウザベンダ間の差異を吸収し抽象化して操作するために WebDriver を使うのが常識でした。現在ではブラウザ側が足並みを揃えてこれをサポートするようになったことで、テストツール側がこれを意識する必要性がなくなりました。TestCafe もこれを前提に設計されています。

BDD ( Behavior Driven Development ) に則ったテストコード

UI 操作手順をメソッドチェーン形式で記述します。これにより、ユーザ操作や外部仕様をまるで自然言語のようにコードで表現できます。つまり テストコードがそのままテスト仕様書となるわけです

サポートブラウザが豊富

  • Google Chrome: Stable, Beta, Dev and Canary
  • Internet Explorer (11+)
  • Microsoft Edge (legacy and Chromium-based)
  • Mozilla Firefox
  • Safari
  • Google Chrome mobile
  • Safari mobile

驚くことに IE11 もサポートしています。また、スマートフォン用ブラウザもサポートしており、これらはテスト実行時に発行される URL および QR コードをスマホから読み込むことでテストが実機上で実行されるという仕組みです(※ 後述)。

BrowserStack との連携をサポート

E2E テストの実行環境は開発マシンとは別に用意しておくことが望ましいです。そうすることでシステム開発(実装)と並行して E2E テストを実施しやすくなります。定期検診のようなイメージです。

TestCafe はテスト実行環境としてローカルマシンだけでなく BrowserStack 上にあるブラウザを指定できます。詳細につきましては次回以降のエントリにて解説予定です。

Getting Started

初めてということで公式ドキュメントにある Getting Started をなぞってみるとします。せっかくなので今回は TypeScript で書いてみましょう。

セットアップ

セットアップは npm もしくは yarn で行います。適当な名前で空ディレクトリを作成して以下を実行してください。

npm

$ npm init
# ...(適当に初期値を入力)
$ npm install --save-dev testcafe

yarn

$ yarn init
# ...(適当に初期値を入力)
$ yarn add -D testcafe

テストコードを書く

以下のテストケースをコードに落とし込みます。

  1. Developer Express 社が公開する TestCafe 用デモページにアクセスする
  2. フォームの Your name 欄に適当な名前を入力する
  3. submit する
  4. 完了ページへ遷移し、入力した名前が表示されるのを確認する

作成したプロジェクト上でファイルを新規作成し、下記のコードを記述します。

import 'testcafe';
import { Selector } from 'testcafe';
// 1.
fixture('Getting started').page('http://devexpress.github.io/testcafe/example');
test('My first test', async (t: TestController) => {
  await t
    .typeText('#developer-name', 'wakamsha') // 2.
    .click('#submit-button') // 3.
    .expect(Selector('#article-header').innerText) // 4-a.
    .eql('Thank you, wakamsha!'); // 4-b.
});

はじめに fixture メソッドで "フィクスチャ" を宣言(定義)します。フィクスチャとはカテゴリのようなもので、テストケースをここに分類していきます。タグ付きテンプレートリテラル構文で書くこともできます。

// どちらでも OK
fixture('Getting started');
fixture`Getting started`;

fixture メソッドの戻り値から page メソッドを実行します。これはこのフィクスチャの開始ページを宣言するというもので、今回は TestCafe 用デモページの URL を渡します。こちらもタグ付きテンプレートリテラルでも書けます。

いよいよテストケースを書きます。 test メソッドでケース名とテスト内容(UI 操作手順)を記述します。第一引数にケース名を渡し、これがそのまま実行ログに出力されます。わかりやすい名前を付けましょう。第二引数にテスト内容を関数として記述します。関数は戻り値が Promise 型となっているので、 async/await を使います。

テスト内容は、この関数の引数に渡される TestController オブジェクトから各種操作に応じたメソッドを呼び出すことで定義します。それぞれのメソッドは TestController を戻り値として返すのでメソッドチェーン形式で記述できます。これにより、まるで自然言語のように直感的で可読性の高いテストコードが実現できるというわけです。

.expect(Selector('#article-header').innerText)
.eql('Thank you, wakamsha!');

最後の二行は、実行されたアクションの結果を確認するものです。 expect メソッドの引数に検証したい対象を渡し、続くアサーションメソッドで対象の検証を行います。今回は #article-header という element の文字列が Thank you, wakamsha! と等しいかどうかを検証します。

ESLint を導入している場合は次のグローバル変数を許可する必要がある

TestCafe では testfixture という関数を使いますが、これらは ESModule の import ではなくグローバル変数として参照することとなるため、そのままですと Linter に不正な参照としてエラー扱いされてしまいます。そのためこれらを許可するべく下記を .eslintrc に追記します。

{
  ...
  globals: {
    test: "readonly",
    fixture: "readonly"
  },
  ...
}

テスト実行

いよいよテスト実行です。プロジェクトの package.jsonscriptstest コマンドを追記してコマンドラインからテストを実行できるようにします。

{
  ...
  "devDependencies": {
    "testcafe": "^1.7.0"
  },
  ...
  "scripts": {
    "test": "testcafe chrome test1.ts"
  }
}

testcafe コマンドに実行環境(ブラウザ)と実行ファイルのパスを指定します。これら2つのオプションは必須です。ここでは chrome を指定しています。ちなみに chrome もしくは firefox に対し :headless というパラメータを付けると "ヘッドレスモード" でテストを実行できます。

ターミナルで yarn test を実行します。

$ yarn test
yarn run v1.21.1
$ testcafe chrome tests/test1.ts
The "src", "browsers" options from the configuration file will be ignored.
 Running tests in:
 - Chrome 80.0.3987 / Mac OS X 10.14.6
 Getting started
 ✓ My first test
 1 passed (2s)
✨  Done in 13.59s.

Chrome が立ち上がり、およそ人力では不可能なスピードでテストが実行されました。終了すると自動でブラウザが閉じ、ターミナルにログが残ります。

リモートブラウザ上でテストを実行する

ホストマシンが Mac や Linux の場合に IE11 でテストを実行したい場合は、ブラウザに remote と指定することでリモートアクセス用の URL を発行できます。

{
  ...
  "scripts": {
    "test": "testcafe remote test1.ts"
  }
}
$ yarn test
yarn run v1.21.1
$ testcafe remote tests/test1.ts
Connecting 1 remote browser(s)...
Navigate to the following URL from each remote browser.
Connect URL: http://192.168.1.6:60424/browser/connect
⠦

同一のネットワーク上にいるマシンからであれば OS やブラウザを問わずこの URL にアクセスすることでテストを実行できます。iOS や Android 端末からもアクセスする際は qr-code オプションを指定することで URL 情報を持った QR コードを発行できます。

{
  ...
  "scripts": {
    "test": "testcafe remote test1.ts --qr-code"
  }
}

設定ファイルを書く

testcafe コマンドにはブラウザや実行するファイル以外にも数多くのオプションがありますが、それらは設定ファイルに記述しておくことが望ましいです。TestCafe にも ESLint や Prettier と同様に JSON5 シンタックス で設定ファイル( .testcaferc )を書くことができます。

{
  "browsers": ["chrome", "firefox"],
  "src": ["tests/**/*.ts"],
  "screenshots": {
    "path": "./reports/screenshots",
    "takeOnFails": true
  }
}

コマンドラインから指定できるブラウザは一つのみでしたが、設定ファイルですと配列で複数のブラウザを指定できます。この場合は Chrome と Firefox が同時に起動します。また all と指定すると、ホストマシンにインストール済みでサポート対象のブラウザ全てが起動します。

screenshots はその名の通り実行状況のスクリーンショットを摂る指定です。path でスクリーンショットを保存するベースディレクトリを指定し、takeOnFailstrue にするとテストが失敗する度に摂るようになります。この他にも実に多くの設定項目が用意されていますので、ぜひともいろいろと試してみてください。

E2E テストを書くにあたって留意しておきたいこと

決して全てのマニュアルテストを完全に置き換えようとすべきではありません。例えば API との疎通確認のためだけに E2E テストケースを用意すべきではありません。その程度ならわざわざブラウザを経由せずとも Unit Tests でカバーできるはずです。

E2E (ないし UI Tests) のケースは Unit Tests とは比較にならないほど複雑であり、エラーやコーナーケースまでも含めるとその数も膨大なものとなります。当然保守コストも跳ね上がります。開発部門とは別にテスト専門(≒ 品質管理特化型)のエンジニアチームを有するような組織ならともかく、並の組織ではなかなか厳しいでしょう1)スタディサプリ ENGLISH 開発組織にもテストを専門としたチームはいません。

出典: The Practical Test Pyramid

上図は各種テスト間の関係性を視覚化したものです。ピラミッドの上に行くほど保守コストが高くなるため、なるべく数を少なく保つすべきであると謳っています。また、この図には描かれていませんが、ピラミッドの更に上には "マニュアルテスト" という概念が存在しています。つまり最後は必ず人の手・目で確認することが重要であるということです。

E2E テストケースは正常系パターンや変更の少ない箇所に限定するようにしてください。マニュアルテストを完全に無くすことを目指すのではなく、あくまで補助ツールとして認識しておくことが大切です。

参考

脚注

脚注
1 スタディサプリ ENGLISH 開発組織にもテストを専門としたチームはいません。