astamuse Lab

astamuse Labとは、アスタミューゼのエンジニアとデザイナーのブログです。アスタミューゼの事業・サービスを支えている知識と舞台裏の今を発信しています。

Web画面自動テストフレームワーク「Geb」の紹介

どうも、えいやです。

今回の弊社技術ブログを担当させていただきます。

会社では主に、ゴロゴロしたり、Asamuse.comのバックエンドのJavaを書いたり、AstaID.comのバックエンドとフロントエンドをJavaやJavaScriptで実装したり、他のWebアプリをたまにメンテしていたりしています。

その他には、

  • GitやJenkins、Redmine、テストフレームワーク、ビルドシステムなどを連動させた開発業務のフローを設計・導入したり
  • fluentdで集めた弊社Webサービスのアクセスログを、Elasticsearchに集約・解析する仕組みを設計、導入、維持したり
  • 解析したアクセスログをサービスとしてコンテンツにフィードバックさせる仕組みを作ったり
  • 発明者や出願人となっている法人の名前を、適当な文字列処理と判定アルゴリズムを書いて名寄せしたり
  • (9割がた趣味として)弊社のサービスと自然言語処理を組み合わせた外部サイトのニュースの分析システムを試作したり

などの仕事もやっています。

必要そうなものがなんとなく崩壊しない程度のレベルを維持しつつ、できそうな感じの方法でとりあえずやっているので、誰かちゃんとしたスペシャリストに任せてもっと安心してゴロゴロしていたい気持ちでいっぱいです。

よろしくお願いします。

記事の概要

さて、今回は弊社においてインテグレーションテストやシステムテストのために採用しているWeb画面自動テストフレームワークの「Geb」について、ハンズオンできる感じでわりと詳し目に紹介します。

Gebはブラウザ自動化ツールとして有名なSeleniumGroovyで記述できるようにしたフレームワークです。

この記事を読むにあたってGroovyやSeleniumに関する詳しい知識は必要としませんが、Javaについてある程度の理解があると読みやすいと思います。

また、Gebでのテストは、テスト対象のWebアプリケーションと完全に独立して作れますので、テスト対象のWebアプリケーションがJVM上で動作している必要はありません。

とはいえ、テスト対象がJVM系のものであれば、そのコードをGebからも利用できるのでJVM系の開発に対して有利です。

逆をいえば、現在JVM系のWebアプリのテストをJSやRuby、Pythonなどのコードで行っている場合には、Gebは乗り換え先の選択肢として有力になりえます。

なお、この記事は、先に筆者が個人として2014年に公開した Qiitaの記事*1から一部を抜粋し、加筆したものであり、過去の記事の執筆時点からのGebおよびSeleniumの変更点に対応したものです。

元の記事は、テストとしての体裁を整えることを主題としています。この部分については、元記事執筆時点から今までの間にはフレームワークの仕様に大きな変更はありません。

そのため、今回この記事では過去の記事との重複を極力避け、導入についての注意点とGebの機能と具体的な使い方に重点を置いて説明します。特に、今回の追記部分でフォームへの入力についての例は、初心者がGebやSeleniumを使う上でのコツやヒントになると思います。

テストとしての体裁の作り方が気になる方は、元の記事*2を参照してください。

Groovyについて

今回の紹介記事ではプログラムコードの全てをGroovyを使って記述しますので、最初にGroovyを知らない方に向けて簡単にGroovyの紹介をしておきます。

Groovy言語は、Java言語を動的型付け言語として簡素で柔軟な記述ができるように設計しなおしたような言語で、Javaの実行環境であるJVM上で動作します。

GroovyはJavaとまったく同じコードで書くことができます*3

加えて、

  • 末尾セミコロンやメソッド引数であることが自明なカッコの省略
  • ダブルクォート囲みでの変数の文字列展開(GStringリテラル)
  • バックスラッシュをエスケープ文字として認識しないスラッシュ囲みの文字列リテラル
  • 簡潔なクロージャの定義構文、ファーストクラス関数として扱えるクロージャオブジェクトなど関数言語的な文法
  • スクリプトとして実行が可能

などといった簡素に書ける仕組みが盛り込まれています。

また、Groovyには強力なメタプログラミングの機能が備わっており*4、Javaではやり難い、言語自身の文法の定義を変更してやりたいことに合わせた記法を作る、DSL*5が作りやすいという特徴があります。

今回紹介するGebは、Seleniumを扱うことに特化したGroovyのDSLです。

Groovyで作られたDSLの有名な例として、Androidアプリケーションのビルド自動化ツールとして広く使われているGradle*6があげられます。

弊社では、今回紹介するGeb以外に、Javaアプリケーションの単体テスト(Spock)やビルド(Gradle)、一部のバッチ処理にGroovyを採用しています。

環境の確認

今回の紹介記事が前提としている環境について、事前のインストールが必要なものと、注意が必要なものを記載します。

不具合の出るJavaバージョン

JDKのバグにより、java 7u71、またはそれ以前の近いバージョンの場合でGebの実行ができないことがわかっています。 このバグはu75以降で修正されています*7

このバグは以下のエラーを引き起こします。

Bad <init> method call from inside of a branch

なお、執筆時の筆者のJava環境は以下となっています。

aya_eiya$ java -version
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)

Groovyの実行環境の準備

Windowsでは、ダウンロードサイトからZIPをダウンロードして解凍するとGroovy環境が丸ごと入ったディレクトリができます。 コマンドプロンプトから実行したい場合は、ディレクトリ中の/binディレクトリにパスを通してください。

MacOS,Cygwinを含む*nix環境では、それぞれのパッケージ管理システム、またはsdkmanを使ったインストールが便利です。 各自好みの方法でGroovyをインストールしておいてください。

GroovyといいsdkManといい、サイトのトップのセンスがヒップでポップですが、使う分にはたぶん影響はありません。

ブラウザの準備

今回の説明では、自動で操作するブラウザをFireFoxとしますので、FireFoxをインストールしておいてください。

FireFoxを自動操作する準備

FireFoxは、2016年6月にリリースされたVersion47.0以降の更新では、以前までのFireFoxで使われていたFireFoxDriverが(訂正:2016/08/03)これまで通りには(ココマデ)使用できなくりました*8

Version47.0以降では、FireFoxをSeleniumで操作するためには、Selenium WebDriverと同等の機能を持つ Marionetteを使います。

MarionetteとはMozillaが開発している、Geckoエンジンを搭載したアプリケーションの操作の自動化を行うことができるツールです。

GebのコードからMarionetteを使うためには、GeckoDriverをインストールする必要があります。

お使いのOS環境に合わせた最新のリリースをダウンロードします。

ダウンロードしたファイルを解凍してできたファイルを、使用OSごとに以下のパスにリネームして保存しておいてください。

Windows

C:¥tools¥geckoDirver¥marionette.exe

※)ダウンロードしたファイルのプロパティを表示して、ブロックの解除を行っておきます。

Mac / Linux

~/tools/geckDriver/marionette

GroovyスクリプトでGebを動かす

まずは、一番簡単な方法でGebを動かしてみましょう。

実行環境の準備

Groovyはスクリプトとして実行できます。

Groovyをインストールすると、Groovyスクリプトを記述して実行できるGUIをもつGroovyコンソールがインストールされます。

Windowsでは、Groovyのインストールディレクトリの下にあるbin/の中にバッチがありますので、Pathを通しておきます。

※nix環境では、パッケージや、sdkManを使ってインストールしておけばパスが通っているはずです。

インストールが終わったら、次のコマンドでGUIのコンソールが立ち上がります。

$ groovyConsole

※)注意:システムのデフォルトエンコードによっては、日本語が文字化けする可能性があります。その場合、次のようにJVM環境変数を設定します。

# *nix
$ export JAVA_OPTS='-Dfile.encoding=UTF-8 -Dgroovy.source.encoding=UTF-8'

#windows
> set JAVA_OPTS='-Dfile.encoding=UTF-8 -Dgroovy.source.encoding=UTF-8'

Gebを実行してみる

GebでFireFoxを操作する一番簡単なコードは以下のような形です。

Browser.drive(driver:new MarionetteDriver()) {
    go 'https://www.google.co.jp'
}

上のコードに、実行に必要な設定を加えたものが下記のスクリプトです。

Groovyコンソールの編集エリアに貼り付けて、メニューの Script > Run(または Ctrl+r )で実行してみてください。

@Grab('org.gebish:geb-core')
@Grab('org.seleniumhq.selenium:selenium-java')

import geb.Browser
import org.openqa.selenium.firefox.MarionetteDriver

{->
  def isWindows = System.properties['os.name'].toLowerCase().contains('windows')
  def home = System.properties['user.home']
  System.setProperty('webdriver.gecko.driver',
  isWindows ?
    /c:\tools\geckoDriver\marionette.exe/:
    "${home}/tools/geckoDriver/marionette"
  )
}()

Browser.drive(driver:new MarionetteDriver()) {
    go 'https://www.google.co.jp'
}

このスクリプトは、FireFoxを立ち上げてGoogle(日本)のページを表示します。

最初の2行はGroovyの機能の一つで、Grapeというライブラリ依存の解決を行う機能です。簡易なMavenやGradleのようなものと考えてください。

GrapeのGrabアノテーションの設定により、Gebフレームワーク本体とJava用のSelenium APIをインターネット上の公開リポジトリからダウンロードして、このスクリプト内で使用できるようにしています。

その後に続くブロックは、Groovyのクロージャ記法で宣言されています。このコードはMarionetteDriverのインストールパスをシステム変数に設定するものです。

このクロージャには名前をつけずにその場ですぐに実行しています。

こうしたクロージャの使い方には、クロージャ冒頭のローカル変数のスコープを限定する以外に意味はありませんが、Gebのコードと分けて説明するためにあえてこのように書いています。

説明のために単体のスクリプトでMarionetteの設定を行っていますが、GroovyConsoleの起動引数として-Dwebdriver.gecko.driver=/path/for/marionetteを設定しても大丈夫です。

起動引数として設定した場合、このクロージャ自体が不要となります。

なお、波かっこで囲まれた部分はJavaではブロックとなりますが、Groovyではifブロックなどを除いて、ほとんどの場合で無名のクロージャの定義となります。

しかしながら、今回はGroovyそのものが主題ではないため、その部分をクロージャとして特に意識しなければいけない場合を除いて、単にブロックと表現します。

Javaにおいてのifやforなどと同じようなものだと思って読んでください。

Browser.driveで始まるブロックがGebによるブラウザの自動化の部分となります。

動作の内容は書いてあるとおり、「ブラウザの動作は、FireFoxを動かすドライバ(Marionette)を指定して、Googleのトップページ(https://www.google.co.jp) に行く」という単純なもので、正常に実行できていればその通りに動いたはずです。

このプログラムが基本となりますが、ここでつまづいてしまうことがあるので、ここで起こりそうな問題を2点あげておきます。

問題 1:Grapeのライブラリダウンロードがエラーを起こす

Grapeを用いた解決では、たまにライブラリ(jarファイル)がダウンロードできない不具合が起こります。

原因と解決方法がいくつかあります。

Grapeの設定ファイルを編集して依存性の解決に使うリポジトリを変更する

セキュリティ設定などで社内環境からは見えないリポジトリがあったり、社内向けにセントラルなどの一部をクローンしてあるリポジトリを見るようになっている場合、この解決方法が良いでしょう。

Grapeの設定ファイル(~/home/.groovy/grapeConfig.xml)に、Grapeが依存性解決に使うリポジトリが書かれていますので、ここに、例えば次のような行を追加します。

<ibiblio name="m2central" root="http://central.maven.org/maven2" m2compatible="true"/>
<ibiblio name="my-repo-snapshot" root="https://my-repo1.net/snapshots/" m2compatible="true"/> <!-- 社内向け -->
<ibiblio name="my-repo-release" root="https://my-repo1.net/release/" m2compatible="true"/> <!-- 社内向け -->
ローカルm2リポジトリが壊れている

Grapeは最初にローカルm2リポジトリ(~/home/.m2/)を参照します。このリポジトリが壊れていたり、Ivyプロジェクト*9に変換できなかったりすると、ダウンロードが失敗したというエラーになります。

リポジトリが壊れている場合は、ダウンロードに失敗しているライブラリをm2リポジトリからフォルダごと削除します。

また、m2リポジトリの中にJarファイルが置いてある場合は、自分でJarファイルをGrapeのキャッシュディレクトリ(~/.groovy/grapes/.../jars/)に保存しても解決できます。

Grapeを使わない

Gradleプロジェクトとして、Grapeを使わずに依存性を解決するとうまくいくことがあります。

問題 2:ブラウザが立ち上がってこない

いくつかの原因が考えらえます。

立ち上がったブラウザを閉じた

GroovyConsoleを使っている場合には、自動的に立ち上がったブラウザは消さないほうが良いです。もし消してしまった場合は、メニュー > Script > Clear Script Contextでコンソールの状態を初期化してください。

運悪く動作しない組み合わせになった

今回の説明では、作業用のPCではFireFoxが自動的に最新状態にアップデートされている想定ですので、SeleniumやGebも最新のものを取得するようにしています。

ブラウザとライブラリの両方が最新の状態であれば大抵は動作しますが、たまに動作がうまくいかない組み合わせになる場合があります。そのときは、ブラウザかライブラリのバージョンを調整してください。

例えば、FireFox 47.0(2016/06/07 リリース版)ではブラウザ側にバグがあり、Seleniumが動作しないという不具合が報告されています。FireFox 47.0を使っている場合は、FireFoxのアップデートを待つか、一つ古いバージョンに戻す必要があります。

プロセスが残ってしまっている

今回の説明では、実行完了時にブラウザが閉じられると確認しづらいのでdriverやBrowserインスタンスをclose()していません。

この状態でも動くはずですが、MarionetteやFireFoxのプロセスが残ってしまうこともあるようです。プロセスが大量に閉じられずに残っている状態だと、ブラウザが起動しなかったりします。

GroovyConsoleを使っている場合はGroovyConsoleを閉じてください。

それでプロセスが消えるはずですが、もし残っているプロセスがあればKillしてください。

Groovyを直接スクリプトとして実行している場合は、最後尾に次のような無限ループを追加しておき、ブラウザを閉じる代わりにctrl+cでスクリプトを終わるようにするとプロセスは残らないはずです。

while(true){
  sleep(1000)
}

Gebの機能の基本

Gebを動作させることができたら、Gebの機能を試していきましょう。

要素をCSSセレクタで取得してみる

記事の簡便のため以降の例では、上記のスクリプトで示したBrowser.driveなどの変更があるブロックのみを載せますので、手で書き換えながら進めてください。

また、必要に応じてimportが出てきます。それらは上部のimport部分に追加してください。

では、以下のスクリプトを試してみましょう。

Browser.drive(new MarionetteDriver()) {
    go 'https://www.google.co.jp'
    println $('input')
}

このスクリプトでは、前の例に加えて「要素をセレクタ'input'で取得して表示する」という行が追加されています。

このセレクタの部分には、CSS3セレクタとほぼ同じものが使えます。

また$という記法から連想できるとおり、jQueryと似た感じで要素の取得や 、取得した要素への値の設定が行えます。

要素の属性を取得してみる

さらに、取得したエレメントの属性を取得してみます。

Browser.drive(driver:new MarionetteDriver()) {
    go 'https://www.google.co.jp'
    $('input').each {
      println it.attr('name')
    }
}

先ほど追加した内容を少し書き換えて、「セレクタ'input'に一致する要素を取得して、その全ての要素についての'name'属性を表示する」ようにしてあります。

「セレクタ'input'に対し該当する要素が複数あるため、取得した要素はリストとなっていること」、「eachがリストのそれぞれの要素に操作を行うメソッド」「操作はブロック(クロージャ)で与えられること」が分かれば難しくはないはずです。

なお、変数itはGroovyの文法で決まっているクロージャ内のスコープに定められる自動変数で、ここではリストの各要素を示すものです。

次のように、itを用いずにクロージャ記法で任意の変数名を明記することもできます。

$('input').each {elem->
  println elem.attr('name')
}

itはその時々により何が設定されているかが変わりますので、クロージャの記述が長くなったり、クロージャのネストが深くなったりする場合は、変数に名前をつけたほうがよいでしょう。

input要素の値を変更する

次は検索窓に検索単語を入れて検索する例です。

少しペースを上げて2つのことを同時に紹介します。

import org.openqa.selenium.Keys

Browser.drive(driver:new MarionetteDriver()) {
    go 'https://www.google.co.jp'
    $('input#lst-ib').value('astamuse') << Keys.RETURN
}

このコードでは、検索窓として使われているinputエレメントの一つをid(lst-ib)で特定して、その値をvalueメソッドで指定しています。

valueメソッドには、引数ありのものと引数なしのものがあります。引数がある場合は値を設定し、引数がない場合は値を取得します。

引数付きのvalueメソッドは、メソッドチェーン的な記述ができるようにメソッドの持ち主のエレメントを返します。

Gebではエレメントに対して、左シフト演算子(<<)を用いて、文字列やKeysに設定されているキーを送信することができます。

この例では、検索文字列'astamuse'を検索窓に入れて、そのままReturn Keyをストライクしたように振舞います。

うまくいっていれば、検索結果が表示されるはずです。

うまく動かない場合は、FireFoxのElementInspectorなどを使って要素の仕様が変更されていないかなどを調べて、適宜コードを書き直してみてください。

ここまでの例でGebの大体のところがつかめたと思います。

複雑なフォームの自動入力

先ほどの例では、Return Keyの送信によってフォームのサブミットを起こして検索ページへの遷移を行いました。

ここまでのことで大抵のことはできるようになったはずです。

では最後の例として、同じ画面の中でGUIのイベントを次々と起こす、見た目にわかりやすいブラウザ自動化を作ってみましょう。

対象となるサイトを、ここまでのGoogleのサイトから、弊社製のJSフレームワークであるAsta4.jsのサンプルサイトに変更します。

対象は、JSで動くインタラクティブなユーザ情報の登録フォームです。一人分の情報を入力したあとにAddボタンを押下するとページ下部に設置されたテーブルに一行追加されるという単純な作りのものです。

このページでは、サーバーへの情報の送信や保存はされませんので自由に試してもらって構いません。

イベントを実行させるわけですが、JSで動くページでは、イベントを引き起こすためには、ページの状態が望んだ状態になるまで待つ必要があることにも注意しましょう。

次のコードを実行してみましょう。これまでより少し長いです。

import org.openqa.selenium.Keys

def makeDataFor = {num->[
  name      :"User-$num",
  bloodType :['A','B','AB','O'][num%4],
  sex       :['0','1'][num%2],
  languages :[['Japanese'],['English'],['Chinese'],['English','Japanese']][num%4],
  'private' :[false,true][num%2],
  desc      :'hoge ' * num
]}

Browser.drive(driver:new MarionetteDriver()) {
    go 'http://astamuse.github.io/asta4js/examples/userList/userList.html'
    waitFor {
      $('select#bloodType>option').find{
        it.attr('value')=='O'
      }
    }
    def rowCount = 0
    10.times {
      def data = makeDataFor it
      $('select[name="bloodType"]') << data.bloodType << Keys.TAB
      $('input[name="sex"][value="' + data.sex + '"]') << Keys.SPACE
      $('.x-lang-group>label').findAll{
        it.text() in data.languages
      }.each{
        it.click()
      }
      data.private && $('label.onoffswitch-label').click()
      $('textarea[name="desc"]') << data.desc << Keys.TAB
      $('input[name="name"]').value(data.name) << Keys.TAB
      $('button#addBtn').click()
      rowCount++
      waitFor {
        $('.x-row').size() == rowCount
      }
      driver.executeScript('scroll(0, 1280);')
      sleep(1000)
    }
}

ブラウザが起動すると、フォームの入力が行われてデータの登録がされるという自動の操作が10回繰り返されます。

このコードでは、最初にmakeDataForというクロージャを定義して、フォームに入力するデータを作る準備をしています。とりあえず、このクロージャの実装は気にせずにいてください。数値を引数としてとって数値ごとに違うフォーム用のデータを返す処理が簡単に書いてあります。

これまでと同じく、Browser.driveで始まる部分からがブラウザに行わせる振舞いです。

指定したURLを表示させる部分は説明した通り、Googleのトップから対象のURLに変更してあります。

次の行で現れるWaitForは、与えたブロックの状態になるまで最大5秒間待ちます。内容は、今回の対象のページで読み込まれるべきJSが読み込まれ、画面が初期化された状態を想定した内容が書いてあります。

rowCountは後でイベントが成功したかどうかを確認するために準備した変数です。

10.timesは続くブロックを10回繰り返す操作です。

この操作と先に準備したmakeDataForを組み合わせて10回違うフォームデータを登録する操作を行います。makeDataForに渡しているitは、ここでは0から始まる何回目の繰り返しかを示す数値です。

繰り返させる動作は、当然フォームへの入力と入力した情報の確定です。

入力項目の種類には、Text、Select、RadioButton、CheckBox、カスタム入力項目、TextAreaが用意されています。

正常に入力を行うには、それぞれの種類にあったイベントを起こしてあげる必要があります。

この部分はブラウザや使うSeleniumのドライバごとに差異があったり、クライアントスクリプト(ひいてはそれを実行するマシン環境)の癖によって安定しなかったりするので、安定させるにはどうしても試行錯誤が必要です。

今回は、最初に名前の入力をさせると動きが怪しくなるという謎の知見が得られました。(そんなに深掘りしませんでしたが、Marionetteドライバの不具合な気がします)

Select項目(単一選択)

$('select[name="bloodType"]') << data.bloodType << Keys.TAB

要素をname属性で選び、上のように値を左シフト(左シフト演算子(<<))で送ると項目の中から値が選択されます。

ただし、この振舞いはブラウザごとの差異が大きいので、Selectについては安定する方法を自分で探したほうが良いです。

なお、Tab Keyを送っているのは、一行ごとでJavaScriptの値変更イベントを起こさせるためです。デバッグ時に、JSとGebのコードを同期しやすくする目的があります。

※) Selectには複数の選択を行うものもありますが、今回は説明しません。

RadioButton項目

性別の項目は、単一選択項目のRadioButtonです。

$('input[name="sex"][value="' + data.sex + '"]') << Keys.SPACE

RadioButtonの場合、セレクタで値のつけられた要素を選択して、Space Keyを送ることで選択できます。

今回の対象では性別の項目では、値と関連付けられたラベルのテキストが同じではないので、値を使って選択しました。

ラベルで選択したい場合は、値とラベルの対応表を持つなどして、ラベルを選択してclick()しても選択できます。

CheckBox項目

言語の項目は複数選択可能なCheckBoxです。

$('.x-lang-group>label').findAll{
  it.text() in data.languages
}.each{
  it.click()
}

CheckBoxには値とラベルのテキストが一致していますので、ラベル要素のうち、そのテキストが選択したい言語のリストに含まれているものを見つけ、それらの要素全ててclick()することで選択します。

カスタム入力項目

data.private && $('label.onoffswitch-label').click()

JSを使って作りこんだカスタム入力項目では、特定のやり方というのはありませんので、条件分岐やclick()などのイベントを駆使して値を設定します。

TextArea項目/testInput項目

名前と詳細のはテキスト入力項目です。

$('textarea[name="desc"]') << data.desc << Keys.TAB

のように要素にたいして左シフト演算子(<<)で文字列を送信しても、

$('input[name="name"]').value(data.name) << Keys.TAB

のように要素の値を設定するvalueメソッドを使ってもよいです。

ボタンクリック

$('button#addBtn').click()

普通に選択してclick()で大丈夫です。

イベント完了待ち

特にClickイベントを起こした後は大体、ページの状態が変化します。 先に説明したwaitForを使って待ちます。

rowCount++
waitFor {
  $('.x-row').size() == rowCount
}

今回は、ボタンをクリックした後で画面下部に一行追加されるはずなので、それを待ちます。

(おまけ)任意のJSの実行

今回の画面は、ボタンを押したらテーブルの最後に一行追加してくれるのですが、追加していくにつれて画面サイズによっては一番下が見えなくなります。

自動テストとしては問題はないのですが、今回は目で見ていますのでそれでは困ります。

次のようにdriver.executeScriptを使えば、任意のJSを実行できるので、今回はそれを使って画面の下のほうに強制的にスクロールしています。ついでに次の入力までに1秒間のスリープを入れています。

driver.executeScript('scroll(0, 1280);')
sleep(1000)

上記までの操作を10回繰り返します。

自動化されたブラウザの動きって眺めているだけでわりと楽しいですよね。

まとめ

さて、以上でGebの紹介は終わりとなります。

今回の記事で、Gebによるブラウザ画面の自動化の雰囲気はわかってもらえたかと思います。

今回の紹介では、導入時点で必要な環境や起こりうる問題について大きく記事の文面を割いて説明しました。実際にこれらをちゃんと確認しないと動かないです。

今日、世の中では普通にたくさんのWebアプリケーションが動いていますが、手元で使っているそれは、マシン環境、ブラウザ環境、ネットワーク環境などのクライアント環境。ミドルウェア環境、フレームワーク、アプリケーション、ネットワークといったサーバ環境。それら全てが正常に連動して初めて動くものです。その構成要素それぞれが、ユーザや開発者の意図に関わらず不定期かつ頻繁に変更されます。

これで問題が発生しないほうがおかしいですね。

「なんかわからんけど突然動かなくなった」とか、「不具合を調べようとしたら何もしてないのに不具合が出なくなっていた」とかの現象に出会うのはもはやチャメシ・インシデント*10ですね。

そういう報告がくる前に問題をいち早く自分たちで発見できるように、インテグレーションテストやシステムテストを常に自動で動かせる状態にしておくことについて、意義は大きいといえるでしょう。

しかし、環境も含めて全てテストしろ、とかだと流石に暴論です。

今回はGebを紹介したわけですが、こいつで自動化していても全ての環境が試せるわけでもありませんし、そもそもこいつやSeleniumが不具合を抱えていたりもします。

とはいえ、どの辺まで頑張って、どの辺から諦めるのかってのを決めておくためには指標となるものがあったほうがいいでしょう。

こうしたらいいとかいう答えを私が持っているわけでも、気合をいれて探そうとしてるわけでもないですけど。

Gebに限らず結合した状態でのテストは実に複雑で、わがままで、突然動かなくなったりするので、マジでテスト項目から外したくなることも多いです。

  • Gebを使っていない環境をGebでテストすることはできません。
  • WebDriverが挟まるとブラウザの挙動が変になることがたまにあります。

とりあえず、こんな面倒ごとを粛々とやってくれる人がいたらいいなぁ、と思いながら、今日も先日から息をしていないGebテストを直しています。

では、本日は以上となります。

(訂正及び追記)

(追記:2016/08/03)

org.openqa.selenium.firefox.MarionetteDriverは非推奨(deprecated)になっているようです。

以下のようにmarionetteを有効にしたうえでFireFoxDriverを使用する方が推奨されています。

import org.openqa.selenium.remote.*
import org.openqa.selenium.firefox.FirefoxDriver

DesiredCapabilities capabilities = DesiredCapabilities.firefox()
capabilities.setCapability("marionette", true)

Browser.drive(new FirefoxDriver(capabilities)){
}

Copyright © astamuse company, ltd. all rights reserved.