こんにちは、ウェブ開発担当の谷口です。
皆さん、テストは好きですか?私はテストよりコードを書くほうが好きなので、いつも自動でテストが回っていればいいなと考えています。そんな人間のために、Selenium や CasperJS 等、世の中には自動でウェブアプリケーションのテストを実行してくれるツールが存在します。特に Selenium は CasperJS と比べクロスブラウザにも対応しており、利用者が多く、情報もネット上にたくさんあるため他に比べて取っつきやすいものとなっています。
ですが、Selenium を使った自動テストの運用がうまく回っているという話はあまり聞きません。今回は、Selenium を使った自動テストがうまくいかない原因と、その対策の一例を共有したいと思います。
そもそも Selenium とは
ウェブブラウザを使ってウェブアプリケーションをテストするツールです。現在も開発が進んでおり、Selenium 次期メジャーバージョンの Selenium 3 が今年のクリスマスに公開される予定です。 Selenium 1(Selenium Remote Control) のころに比べ、現行の Selenium 2(WebDriver) ではかなり動作が安定し、これからも楽しみなツールとなっています。
画面自動テストがうまくいかない原因
結論から言うと『コストが高い』という1点に尽きます。特定のブラウザを自動で自在に動作させることはさほど難しくありません。難しいところは Selenium がやってくれます。ではなぜコストが高いのでしょうか。代表的な原因を3つ紹介します。
原因その1.テストケース記述コスト
まず画面を自動で動作させるには、どのように動作させるのかといった、テストケースを作成する必要があります。これはほとんどの場合、Java で書くことになります。開発者ならまだいいのですが、評価担当者やデザイナーなど、普段 Java を書かない人にはこの時点で敷居が高く、かつコードとしてメンテナンスしていかなければならないので、保守コストがかかります。
原因その2.マルチブラウザ対応コスト
ほとんどのウェブアプリケーションはマルチブラウザ対応なので、当然、対応する全てのブラウザでのテストが必要です。たとえば、とあるボタンをクリックしたいとします。
driver.findElement(By.id("hoge")).click();
このように記述すれば、Selenium は id が hoge であるコンポーネントに対してクリック動作を行いますが、それは Firefox や Chrome であって、IE8 では動作しません。IE の場合は
driver.findElement(By.id("hoge")).sendKeys("\n");
このようにして、コンポーネントに対してエンターキー押下イベントを発生させるといったように記述する必要があります。これを全てのテストケースのクリック部分で実装しなければならないので、あらゆるところで分岐が発生し、テストケースである Java はごちゃごちゃしてしまい、保守コストがあがってしまいます。
原因その3.パラメータ変更テストによるコスト
テストでは、同じ画面に対して、何パターンもの入力値をチェックするのが普通です。べた書きすると、テストケースの数だけ同じような処理を記述する必要があります。
//山田 太郎と入力できているかテスト driver.findElement(By.id("myoji")).sendKeys("山田"); driver.findElement(By.id("namae")).sendKeys("太郎"); assertEquals(driver.findElement(By.id("myoji"))).getText(),"山田"); assertEquals(driver.findElement(By.id("namae"))).getText(),"太郎"); //長い名前が入力できているかテスト driver.findElement(By.id("myoji")).sendKey("山田山田山田"); driver.findElement(By.id("namae")).sendKey("太郎太郎太郎"); assertEquals(driver.findElement(By.id("myoji"))).getText(),"山田山田山田"); assertEquals(driver.findElement(By.id("namae"))).getText(),"太郎太郎太郎");
ここからさらに、ミドルネームも入力項目として管理する必要が出てきた場合、どうでしょう。考えただけでも大変です。
じゃあどうすれば?
これまでの問題は Selenium をネイティブのまま使うがために発生してしまった問題点と言えます。このような問題を解決する方法として、Selenium をラッピングした FluentLenium というツールがあります。
WebDriver の API を実行できるため、Selenium が可能な動作は FluentLenium でも可能であり、ブラウザ間の動作の差異はありません。
jQuery のセレクターのような記述が可能で、Selenium ベースで書くよりもシンプルにテストケースを記述出来ます。下記は先程の Selenium のテストを FluentLenium で記述しなおしたものです。
//山田 太郎と入力できているかテスト fill("#myoji").with("山田"); fill("#namae").with("太郎"); assertEquals($("#myoji").getValue(),"山田"); assertEquals($("#namae").getValue(),"太郎");
これで、ある程度の問題は解決出来ますが、それでも Java で記述する必要があったり、パラメータごとにテストケースを記述する必要が出てきます。それを解決するには自前で FluentLenium をラッピングするツールを作成し、そのツールでテストケースとパラメータを読み取り、 FluentLenium を使ってブラウザを動作させましょう。下記はそのツールの全体イメージとなります。
今回の例では、『どこに何をどうする』という実際の動作を設定しているファイル(テストケースファイル)とその動作に使用するパラメータを記述しているファイル(パラメータファイル)を分割しています。こうする事により、パラメータファイルを変更すれば、様々なテストが実行できるようにしてあるイメージです。
テストケースファイルとパラメータファイルの例です。ツールはテストケースファイルを読み取ってテストを実行します。形式は何でもいいですが、XML にしておけばパーサを使って読みこむことができ、処理が簡潔になります。action に設定されている文字列が動作種別と考えてください。
CSV の行数の数だけ、テストケースファイルに記述されたテストが繰り返し実行されます。また、value には $ から始まる文字列が記述されていますが、これはパラメータファイルのカラム名と紐付きます。 今回の例で言うと、様々な名前を3回入力し、3回入力確認を行います。
長期間に及ぶプロジェクトほどテストに割く時間は長くなるので、どのようにテストしていくかといったことを最初に考えるのもいいかもしれませんね。
フェンリルのオフィシャル Twitter アカウントでは、フェンリルプロダクトの最新情報などをつぶやいています。よろしければフォローしてください!