T-DASHとSelenium/Robot Framework・WebdriverIO/mochaを比べてみた！～共通のテストシナリオで～

# はじめに
この記事は[T-DASHと他のテスト自動化ツールを比べてみた！アドベントカレンダー2021](https://qiita.com/advent-calendar/2021/tdash02 "T-DASHと他のテスト自動化ツールを比べてみた！アドベントカレンダー2021")の21日目の記事として投稿しています。

自動化の分野に興味があり、Selenium/Robot FrameworkをRPA的に本業で使っていたところ、アドベントカレンダーで、**OPENβ版**（新しい）・**ローコード**(クラウドで動作してくれる？？[^run_server])・**見やすい試験結果**（どこか見覚えのある）の**[T-DASH](https://www.t-dash.io/ "T-DASH")**を見つけ、「要チェックや！」ということで、試してみました。

# この記事で伝えたいこと

先にまとめを書いておくと、T-DASHは、Selenium/Robot FrameworkやWebdriverIO/mochaに比べ、

* (A) 画面要素とテストシナリオ/動作定義、(B) 画面名と要素名と設定値の適度な縛りによって、Page Object Patternを実現しやすそう
* 動作が制御/操作と検証（アサーション）にちゃんと分けられていて、テストでアサーションを十分行えそう・意識付けができそう
* ローコードが売りのひとつのようでテスト用途以外にも、環境準備が簡単、シナリオを日本語・定型化された自然言語っぽく書ける、付属ツールで要素指定でのXPath最適候補を取れるので、エンジニア以外でもブラウザ操作に関するRPAとして使えそう

と思えました。

以下、主語デカ・主観・技術領域が微妙に違う可能性もありますが、比較表です。

* ① T-DASH
* ② Selenium/Robot Framework
* ③ WebdriverIO/mocha

|  | ① T-DASH | ② | ③ |
|:-|:-:|:-:|:-:|
| 環境準備 |  |  |  |
| - CLI[^cli]に慣れている場合 | ○ | ○ | ○ |
| - CLIに慣れてない場合 | ◎ | △ | △ |
| 操作(テスト)シナリオ作成 |  |  |  |
| - CLI・テスト自動化に慣れてる場合 | △ | ○ | ○ |
| - CLI or テスト自動化に慣れてない場合 | ○ | △ | △ |
| テスト結果の利用 | ◎ | ◯ | ◯ |

## 環境準備
T-DASHについては、[T-DASHを使ってみた！アドベントカレンダー2021](https://qiita.com/advent-calendar/2021/tdash "T-DASHを使ってみた！アドベントカレンダー2021")の1日目、84zumeさんの記事の通りで、Windows環境で簡単にインストール・利用開始できました。

- 参考：動作環境 AWS 
 - Windows_Server-2022-Japanese-Full-Base-2021.09.15 (ami-0cd446ca6e70639fd)
 - t2.micro
 - T-DASH: 0.1.005b

https://qiita.com/84zume/items/5051531c1d82139b4267

Selenium/Robot Framework、WebdriverIO/mocha については、CLIに慣れている場合、問題なく環境準備できるかと思います。逆に現状Windowsが対象のT-DASHについて、Windows端末を持ち合わせていない可能性等もあり、○としました。

一方で、CLIに慣れていない場合も、公式ドキュメントなどを見れば環境準備はできるかと思いますが、学習コストやハードルがある、また、**T-DASHについては、ブラウザ(Chrome, Firefox)さえインストールしていれば、対応するWebDriverを自動でインストールしてくれた** （アップデートもしてくれるはずな）ので、T-DASHを◎、Selenium/Robot Framework、WebdriverIO/mocha を△としました。

## 操作(テスト)シナリオ作成

本記事のメイン部分です。

共通的なテスト、かつ独自では無いテストシナリオで比較するのが分かりやすそうなので、Seleniumのホテル予約のやつを探したところ、takeya0x86さんがテスト自動化学習向けのデモサイトを公開なされていました。今回はこのデモサイト・WebdriverIO/mochaのサンプルコードを利用させていただきました。

https://takeya0x86.github.io/2020/04/15/automation-testing-practice/

https://speakerdeck.com/takeya0x86/automation-testing-plactice

### WebdriverIO/mochaの場合

WebdriverIO/mochaのテストシナリオは、testplanisphere/hotel-example-webdriverio-ja/test/specs/ 配下にあり、testplanisphere/hotel-example-webdriverio-ja/test/pageobjects/ 配下にディレクトリ名の通りPage Objectがありました。

今回は、ログイン回りの以下の3つのテストケースをT-DASHでも作成してみたいと思います。

#### テストシナリオ

https://github.com/testplanisphere/hotel-example-webdriverio-ja/blob/92a298c777da4a979672922aebf484fcfe4f015a/test/specs/login.spec.js#L5-L37

1. 定義済みユーザ[^user_info]でログインができること
    1. トップページを表示
    1. ログインページへのリンクをクリック
    1. 定義済みユーザのメールアドレス・パスワードを入力しログインボタンをクリック
    1. ヘッダ（h2要素）がマイページであることを検証
1. 未入力でエラーとなること
    1. トップページを表示
    1. ログインページへのリンクをクリック
    1. （空文字をメールアドレス・パスワードに入力し）ログインボタンをクリック
    1. エラーメッセージが想定通り（このフィールドを入力してください。）であることを検証
1. 未登録のユーザでエラーとなること
    1. トップページを表示
    1. ログインページへのリンクをクリック
    1. 未登録ユーザのメールアドレス・パスワードを入力しログインボタンをクリック
    1. エラーメッセージが想定通り（メールアドレスまたはパスワードが違います。）であることを検証

```js
describe('ログイン', () => {
  it('定義済みユーザでログインができること', async () => {
    await TopPage.open();
    await TopPage.goToLoginPage();
    await LoginPage.email.setValue('ichiro@example.com');
    await LoginPage.password.setValue('password');
    await LoginPage.submit();

    await expect(MyPage.header).toHaveText('マイページ');
  });

  it('未入力でエラーとなること', async () => {
    await TopPage.open();
    await TopPage.goToLoginPage();
    await LoginPage.email.setValue('');
    await LoginPage.password.setValue('');
    await LoginPage.submit();

    await expect(LoginPage.emailMessage).toHaveText('このフィールドを入力してください。');
    await expect(LoginPage.passwordMessage).toHaveText('このフィールドを入力してください。');
  });

  it('未登録のユーザでエラーとなること', async () => {
    await TopPage.open();
    await TopPage.goToLoginPage();
    await LoginPage.email.setValue('error@example.com');
    await LoginPage.password.setValue('error');
    await LoginPage.submit();

    await expect(LoginPage.emailMessage).toHaveText('メールアドレスまたはパスワードが違います。');
    await expect(LoginPage.passwordMessage).toHaveText('メールアドレスまたはパスワードが違います。');
  });
});
```

#### 参考：テストシナリオに関連するPage Objectの例（トップページの場合）

https://github.com/testplanisphere/hotel-example-webdriverio-ja/blob/92a298c777da4a979672922aebf484fcfe4f015a/test/pageobjects/top.page.js

```js
class TopPage extends Page {
  get loginLink() { return $('=ログイン'); }
  get signupLink() { return $('=会員登録'); }
  get planLink() { return $('=宿泊予約'); }

  async open() {
    await super.open('/ja/');
  }

  async goToLoginPage() {
    await (await this.loginLink).click();
  }

  async goToSignupPage() {
    await (await this.signupLink).click();
  }

  async goToPlansPage() {
    await (await this.planLink).click();
  }
}
```

### T-DASHの場合

#### (まずはサンプルを動かしてみる)

このあとの事象切り分けも兼ねて、同梱されてたプロジェクトのサンプル_0.1.001bが動くことを確認してみます。

* Windows_Server-2022-Japanese-Full-Base-2021.09.15 (ami-0cd446ca6e70639fd)で、初期ブラウザはEdgeのみだったので、動かず
  * Chromeをインストール(ver 96.0.4664.110)
  * Chromeインストール後、自動でWebDriver(ver 96.0.4664.45)もインストールされた
     * Chromeバージョンアップ時も追従してくれそう
     * WebDriver回り詳しくない場合に親切
* サンプル_0.1.001bのテストシナリオを実行
  * Chromeが起動されてきた
     * headlessブラウザではないのも、リソースは使うが、ブラウザのテスト自動化がはじめての場合は分かりやすそう
  * あれ、どこかで見たページが、、
  * サンプル_0.1.001bも、テスト自動化学習向けのデモサイト https://hotel.testplanisphere.dev/ja/index.html を使われてました
     * ログイン回りの3つのテストケースより充実（3つのテストシナリオで、各3つのテストケース）してます

#### ログイン回りの3つのテストケースの作成

テストケースの作成は、[T-DASHを使ってみた！アドベントカレンダー2021](https://qiita.com/advent-calendar/2021/tdash "T-DASHを使ってみた！アドベントカレンダー2021")の4日目、84zumeさんの記事の感じで、T-DASHをはじめて30分～1時間程度で3つのテストケースをできました。慣れればもっと早くたくさんのテストケースを作成できると思います。(E2EテストでUnitテストに比べ、処理コストも増えるので使いどころの見極めは必要ですが)

https://qiita.com/84zume/items/c5f5f82e2c6da1a5c1f5

実際に作成したテストケースは、以下です。
![tdash_test_s_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2413423/f784f7aa-dfe9-5e9d-f9d3-2076cb7d2e69.png)

WebdriverIO/mochaの場合と比較すると

```js
describe('ログイン', () => {
```
T-DASHだと、テストシナリオ名に。

```js
  it('定義済みユーザでログインができること', async () => {
    await TopPage.open();
    await TopPage.goToLoginPage();
    await LoginPage.email.setValue('ichiro@example.com');
    await LoginPage.password.setValue('password');
    await LoginPage.submit();

    await expect(MyPage.header).toHaveText('マイページ');
  });
```
T-DASHだと、
![tdash_test_s_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2413423/6993ee79-9f70-e72c-b5d4-881701eb9052.png)

```js
  it('未入力でエラーとなること', async () => {
    await TopPage.open();
    await TopPage.goToLoginPage();
    await LoginPage.email.setValue('');
    await LoginPage.password.setValue('');
    await LoginPage.submit();

    await expect(LoginPage.emailMessage).toHaveText('このフィールドを入力してください。');
    await expect(LoginPage.passwordMessage).toHaveText('このフィールドを入力してください。');
  });
```
T-DASHだと、
![tdash_test_s_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2413423/63b92983-e96a-268f-b066-57721691e670.png)

```js
  it('未登録のユーザでエラーとなること', async () => {
    await TopPage.open();
    await TopPage.goToLoginPage();
    await LoginPage.email.setValue('error@example.com');
    await LoginPage.password.setValue('error');
    await LoginPage.submit();

    await expect(LoginPage.emailMessage).toHaveText('メールアドレスまたはパスワードが違います。');
    await expect(LoginPage.passwordMessage).toHaveText('メールアドレスまたはパスワードが違います。');
  });
```
T-DASHだと、
![tdash_test_s_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2413423/16e89498-1a4c-817e-49fd-60661b11d010.png)

トップページのPage Objectに類似する部分としては、

```js
class TopPage extends Page {
  get loginLink() { return $('=ログイン'); }
  get signupLink() { return $('=会員登録'); }
  get planLink() { return $('=宿泊予約'); }
```
T-DASHだと、今回のテストケースで必要最低限の箇所として
![tdash_page_o_top.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2413423/84ee44e8-d003-4212-8724-e83427e9a3d7.png)

```js
  async open() {
    await super.open('/ja/');
  }

  async goToLoginPage() {
    await (await this.loginLink).click();
  }

  async goToSignupPage() {
    await (await this.signupLink).click();
  }

  async goToPlansPage() {
    await (await this.planLink).click();
  }
}
```
T-DASHだと、これら相当の機能は、デフォルトで組み込み済みの動作の以下を使い、テスト手順を作成できます。

* 画面のURLをブラウザで開く：`「`画面名`」画面のURLをブラウザで開く`
* 遷移リンクをクリックする：`「`画面名`」画面のリンク「`要素名`」をクリックする`
![tdash_tezyun_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2413423/be01a5b9-cff4-5fc1-c7af-58b19c1e0ff6.png)

以上のように、適度な縛り（(A) 画面要素とテストシナリオ/動作定義、(B) 画面名と要素名と設定値）もあることで、テスト自動化やRPAに慣れていない場合でも、一定の形式に即した実装になり、実装のばらつきはWebdriverIO/mochaやSelenium/Robot Frameworkに比べ、減ってくると思います。（T-DASHが○、WebdriverIO/mochaやSelenium/Robot Frameworkが△）

一方で、テスト自動化・CLI等に慣れてくると、GUIでの操作ではテスト条件を振らす際などテストシナリオの作成自体もコード化・効率化したくなってきそうなので、CLIなどでも扱いやすいWebdriverIO/mochaやSelenium/Robot Frameworkを○、T-DASHを△としています。
このあたりは、T-DASHでも、テストケース・シナリオのコピー機能や、エクスポートできるxlsxファイルを加工してインポートしたり、T-DASH自体もWebアプリなのでT-DASH自体をT-DASHで操作するなど色々試せる部分はあるかと思います。


## テスト結果の利用

シナリオ作成・実行後の観点としては、テスト自動化については、テスト結果の履歴を残しておきたいといった観点も出てくるかと思います。（操作の自動化やたとえばWebアプリのマニュアル作成用のスクリーンショット取得などだけで、アサーションをあまり使わないRPA的な使い方では最新版があれば十分かもしれません。）

T-DASHだと、デフォルト設定でテスト結果の履歴が残り、ダッシュボードで概要が見やすく、レポートリンクで各詳細が見れる点がよかったです。

Selenium/Robot Frameworkだと、起動オプションでファイル名にタイムスタンプや出力ディレクトリを指定すれば履歴を残せたり、WebdriverIO/mochaだと、CLIでの出力で、履歴の見やすさやデフォルトでは上書きで最新結果のみになりやすいので、少し劣る部分かと思います。

# まとめ

本記事では、T-DASHを実際に使って3つのテストケースを作り、Selenium/Robot FrameworkやWebdriverIO/mochaと比べてみました。

T-DASHは、Selenium/Robot FrameworkやWebdriverIO/mochaに比べ、

* (A) 画面要素とテストシナリオ/動作定義、(B) 画面名と要素名と設定値の適度な縛りによって、Page Object Patternを実現しやすそう
* 動作が制御/操作と検証（アサーション）にちゃんと分けられていて、テストでアサーションを十分行えそう・意識付けができそう
* ローコードが売りのひとつのようでテスト用途以外にも、環境準備が簡単、シナリオを日本語・定型化された自然言語っぽく書ける、付属ツールで要素指定でのXPath最適候補を取れるので、エンジニア以外でもブラウザ操作に関するRPAとして使えそう

と思えました。

ただし、縛りがあることによって、ブラウザの起動オプションを与えて実行したいなど細かな部分は現状触れなさそうなので、私個人としては利用用途に応じて今後、基本的な操作/検証で済む時は T-DASHを試し、ブラウザの起動オプションを与えたい等特殊な時はSelenium/Robot Frameworkで対応しそうと思いました。（日本語で結果がわかりやすいのも結構重要だと思っているので日本語を使いやすい2択になりました。）

[^run_server]: クライアント（自PC）側でのブラウザ起動でした。実際に試した＋ https://www.t-dash.io/pricing/ のT-DASH機能比較 - サービス提供形態より
[^cli]: Command Line Interface
[^user_info]: トップページ https://hotel.testplanisphere.dev/ja/ に情報が記載されています。