ContractS開発者ブログ

契約マネジメントシステム「ContractS CLM」の開発者ブログです。株式会社HolmesはContractS株式会社に社名変更しました。

TestCafeにGaugeを組み合わせてBDDできないか試してみた

社内でE2Eテストツールとして、TestCafeの話題が出たため、前回の記事で試したGaugeとの組み合わせでBDDのようにテストを実行できないか、検証してみました。

実行環境

名前 バージョン
OS Windows 10 Pro 64bit バージョン2004
Node.js 14.15.2
Yarn 1.22.5
TestCafe 1.10.0
Gauge 1.1.6

なぜTestCafe単体ではなく、Gaugeと組み合わせようと思ったのか

TestCafe単体ではテストは簡潔に記述できるものの、実際の運用に乗せることを考えると、ログインや特定の状態への遷移といった、よくある処理の共通化は必要であると思います。

TestCafeはメソッドチェーンでテストを簡潔に記述できますが、共通で前処理を行おうと思うと、ツールのサポートとしてはFixtureのbeforeEachメソッドによるフィクスチャ単位の共通化しか見つけられませんでした。

Page Object Patternでも記述できるため、共通処理はPage Objectとして切り出し、フィクスチャやテストから実行することはできそうですが、BDDのように明示的に前処理を記述することは、TestCafe単体ではできないようなので、GaugeないしはCucumber.jsといったツールと組み合わせることで、明示的な処理の共通化ができるか確認できればと考えました。

また、前回はGaugeとJavaの組み合わせを試したため、今回はGaugeとJavaScriptの組み合わせで、使用感に違いがないかも確認できればと思い、Gauge+TestCafeで試してみることとします。

環境構築

TestCafeのインストール

devexpress.github.io

npmまたはYarnでインストールします。*1

インストールが完了したら、 testcafe -v でバージョンが確認可能です。記事作成時のバージョンは、1.10.0でした。

また、 testcafe -b で、利用可能なブラウザ一覧を確認できます。

# npm
$ npm install -g testcafe

# Yarn
$ yarn global add testcafe

# バージョン確認
$ testcafe -v
1.10.0

# 利用可能なブラウザ確認
# Firefox, Google Chrome, Microsoft Edge(Chromiumベース)、およびInternet Explorer 11と旧Microsoft EdgeがインストールされたWindows 10では、以下のように表示されました
$ testcafe -b
firefox
chrome
ie
edge
edge-legacy

Gaugeのインストール

前回の記事 と同様の手順で、Windowsインストーラーをダウンロードし、Gaugeをインストールします。

記事作成時のバージョンは、1.1.6でした。

Gauge+TestCafeのサンプルプロジェクトのダウンロード

getgauge-examples/gauge-testcafe にサンプルとなるGitプロジェクトがあったため、ZIPダウンロードおよび展開します。

npmまたはYarnで、依存モジュールのインストール後、テストを実行します。

# npm
$ npm install && npm test

# Yarn
$ yarn install && yarn test

TestCafeが起動し、Google Chromeのウィンドウが開きます。

起動時の画面

...が、以下のエラーで失敗しました。

Failed Step: Search for "github TestCafe"
Specification: specs\example.spec:6
Error Message: Error: Timed out
Stacktrace:
Error: Timed out
サンプルテストの修正

specs/example.spec がテスト仕様、 tests/step_implementation.js がテスト実装になります。

テスト内容を確認してみると、以下のようになっていました。

  1. Googleを開く
  2. 「github TestCafe」で検索
  3. 検索結果の最初のリンクのテキストに「GitHub - DevExpress/testcafe:」が含まれるか検証

まず、検索する文字列の入力欄を input[title="Search"] で取得しようとしているものの、日本語環境ではtitleが「検索」になっているようです。

また、GitHubの検索結果のリンクテキストが、記事作成時点では「DevExpress/testcafe: A Node.js tool to automate end ... - GitHub」となっているため、検証も失敗します。

それぞれ以下のように変更し、テストを再実行すると成功しました。

  1. 検索文字列の入力欄取得を input[title="検索"] で行うよう step_implementation.js を変更
  2. example.spec から渡される検証用の文字列を "GitHub - DevExpress/testcafe:" から "DevExpress/testcafe:" に変更

前回と同様、 reports/html-report/index.html にレポートが出力されます。

f:id:h-yamamoto_holmescloud:20201225124621p:plain
Gaugeのテストレポート

実装

いきなり躓きましたが、気を取り直して実装していきます。

テストするブラウザの変更

初期状態では、Google Chromeでのテストのみ実行されました。

コマンドからTestCafeを実行する場合、 testcafe ブラウザ名,ブラウザ名,... JavaScriptテストファイル でテストするブラウザを追加できるため、それっぽい設定はないかと確認したところ、 tests/testcafe_init.jsbrowsers('chrome') という設定がありました。調べてみると、APIを使ってJavaScriptから実行できるようです。

browsers(['chrome', 'ie', 'edge']) のように記述すると、各ブラウザのウィンドウは最初に開きますが、先頭のブラウザにのみテストが実行され、それが終わった段階でテスト終了と判断されます。

おそらく、オーケストレーションを行うGaugeと、テストを実行するTestCafeのライフサイクルの違いが原因かと思います。

ひとまず、Google Chrome以外にも、 browsers('ie')Internet Explorerbrowsers('edge')ChromiumMicrosoft Edgeでテストを実行できることは確認できました。

テスト仕様の追加

前回と同じMarkdownを、 specs/search.spec に保存しました。

テスト実装の記述

当初は tests/search_steps.js を追加して検証しようとしたのですが、GaugeではJavaScriptでテストを実装する場合、 tests/step_implementation.js にしかステップを記述できないようです。

ステップ内で実行する関数の内容を別ファイルに書いてimportしたりはできそうですが、ひとまず tests/step_implementation.js に以下を追記します。

なお、変数 _Selector は、それぞれTestCafeのTestController、およびSelectorになります。

step('検索エンジンのURL <url> を開く', async (url) => {
  await _.navigateTo(url);
});

step('検索文字列入力欄 <inputSelector> を取得する', async (inputSelector) => {
  const searchTextInput = Selector(inputSelector).with({ boundTestRun: _ });
  gauge.dataStore.scenarioStore.put('searchTextInput', searchTextInput);
});

step('<searchText> で検索する', async (searchText) => {
  const searchTextInput = gauge.dataStore.scenarioStore.get('searchTextInput');
  await _.typeText(searchTextInput, searchText);
  await _.pressKey('enter');
});

step('検索結果ページのタイトルが <title> であることを確認する', async (title) => {
  const titleElement = Selector('title').with({ boundTestRun: _ });
  const pageTitle = await titleElement.innerText;
  gauge.message(`検索結果ページのタイトル: ${pageTitle}`);
  await _.expect(pageTitle).eql(title);
});

TestCafeでは通常 fixturetest で記述していきますが、Gauge+TestCafeでは、実行時にGaugeのシナリオがTestCafeのfixtureに変換されるようです。

前回Javaで記述した部分をそのままJavaScriptに置き換えただけなので、TestCafeの記述としては洗練されていないですが、この状態で yarn test でテストの実行ができました。

感想

GaugeとTestCafeの組み合わせは、あまり良くはない印象です。

感じたデメリット

GaugeのJavaScriptによるテスト実装では、ステップを step_implementation.js にしか記述できないようです。私の調査不足かもしれませんが、ドキュメントをざっと読んだ限り、変更方法などは見つけられませんでした。

Gauge+TestCafeのサンプルプロジェクトでも、 step_implementation.js から test_controller_holder.js をimportしているため、実装を分割することは可能ですが、テストのステップをすべて単一ファイルに記述することになると、管理が煩雑になりそうです。

また、TestCafeの複数ブラウザによるテストや、並列テスト実行が活かせなくなりました。こちらはTestCafeのメリットを打ち消してしまっています。

TestCafe単体でできたことが、Gaugeと組み合わせたことでできなくなるのであれば、相性が悪いと言って差し支えないかと思います。

Gauge+Javaとの使用感の違い

Gauge+Javaでは複数ファイルにステップ記述が可能なため、そちらと比較するとGauge+JavaScriptは使い勝手が悪く感じました。

GaugeのData Storeに相当するctxがTestControllerに用意されていたりと、TestCafe自体が高機能ということもあり、Javaと組み合わせたときほど利便性の向上も感じませんでした。

TestCafeでBDDするための代替手段

TestCafeをGaugeと組み合わせるのは難しそうなので、Cucumberなど他のツールとの組み合わせが可能か調べてみました。

3年ほどOpenされているTestCafeのissueで、CucumberとTestCafeの統合についての要望が上がっており、そこからたどったissueの Cucumber Integration with Testcafe removed from roadmap? にて、Cucumber Integration がロードマップから削除されたこと、および gherkin-testcafe の使用が推奨されていました。

gherkin-testcafe を用いれば、Gherkin構文でTestCafeのテスト記述および実行ができるため、現時点ではこれを使うのがTestCafe+BDDには最適なようです。

実運用ではPage Objectとしてテスト対象のページをクラス化し、それをGherkinで記述したテスト仕様クラスから操作するのがいいかと思います。

総括

Gauge+Javaではかなりのメリットを感じられたのですが、Gauge+TestCafeではデメリットが目立つ結果となりました。

TestCafe自体の有用性は確認でき、またPage Object Patternによる記述ができることも確認できたため、今後はPage Object Patternによる実装や、 gherkin-testcafe との組み合わせの検証を行いたいと思います。

最後に

Holmesではエンジニア・デザイナーを募集しております。ご興味がある方はこちらからご連絡ください。

lab.holmescloud.com

lab.holmescloud.com

*1:Node.js、およびYarnのインストールについては割愛します