Seleniumの自動テストをCI環境(Jenkins)で快適に実施する

Ruby Advent Calendar 2011 16日目の記事です。

Webアプリの画面テストにはやはりその利便性からSeleniumを使うことが多いだろう。
ちょっとした変更でも問題ないと高をくくっていても、リリース後にリンクやボタンをクリックした途端に500や404が発生した日には目も当てられない。
そうならないために、定期的にSeleniumのテストを流す--自動テスト環境を構築して--ことで、問題をいち早く検知することが大切だ。
今回は、Webアプリにおける画面の自動テスト環境を、Selenium+Ruby(RSpec)+Jenkinsで構築する一例を紹介する。
Seleniumで画面テストを書くときにRSpecの恩恵を得たいので、現在Javaで開発しているプロジェクトも画面テストはRubySeleniumテストコードを書いている。

用意するもの

SeleniumをCI環境で実施するために以下のものを用意しておこう。

Seleniumのテストコード

まずは、Seleniumのテストコードを作成することろから始めよう。

Seleniumテストの構成

依存するgemを定義しておくことでプロジェクトメンバ全体で共有できるので、テストコードのディレクトリにGemfileを用意しておき、bundlerを必ず使うように周知する。

source "http://rubygems.org"

gem "rspec", "~> 2"
gem "selenium-webdriver"
gem "watir-webdriver"
gem "pry"

ちなみに、現プロジェクトのSeleniumテストのディレクトリ構成は、

#{PROJECT_ROOT}/test/selenium
  |
  `- Rakefile
  `- Gemfile
  `- bin/  # テストデータ生成するスクリプト等が置いてある
  `- conf/ # 設定ファイル置き場。各テストクライアントマシンへのホスト名等も一ファイル一環境として作成している
  `- lib/  # selenium-webdriverにモンキーパッチしたものや、DBアクセスやutility系が置いてある
  `- spec/ # specファイル

のようになっている。
# pryを使用しているのはテストを書いているときに、binding.pryで途中で止めてDOMを確認するときに便利だからだ。

watir-webdriverを使う

さらに、watir-webdriverを使う。
これはwebdriverのAPIをラップしたライブラリで、より直観的にRubySeleniumテストコードが書けるようになっている。
たとえば、selenium-webdriverだけで、画面テストを書くと

require 'selenium-webdriver'

@driver = Selenium::Webdriver.new
@driver.navigate.to "http://www.example.com/path/to/test/page"
@driver.find_element(:tag_name => 'div').find_element(:class => 'hoge').
        find_element(:tag_name => 'a').
        click

このように少しコードが冗長になってしまうが、watir-webdriverで書くと、

require 'watir-driver'

@browser = Watir::Browser.new
@browser.goto "www.example.com/path/to/test/page"
@browser.div(:class => 'hoge').
         a.
         click

Watir::Element等便利なラッパーが用意されていることで直観的に書ける。

リモート実行するために

リモート実行を容易にすることもselenium-webdriverを使う理由の一つだ。Seleniumのテストコード側では、ドライバ初期生成時にリモート実行することを明示する。

@browser = Watir::Browser.new(:remote, :url => "http://192.168.1.175:4444/wd/hub", :ie)

これだけで、あとは通常通りにSeleniumテストを書くだけで:urlで指定したクライアントでリモート実行してくれる。

Jenkins側でのコントロール

Jenkins側で、SeleniumテストをRakeでキックするためにRakeプラギンを導入しておこう。
Rakeで各テストのキックをコントロールするためだ。Rakeプラギンを使うことで、JenkinsからRakeの実行が簡単になる。
Rakeプラギンをインストールすると、Seleniumテストを実施するjobの設定項目の'ビルド'でビルド手順の追加プルダウンから'Invoke Rake'が追加されている。

Rakeプラギンで設定できる項目として、

  • Rakeのバージョン
  • Rakeのタスク
  • Rakefile
  • Rakeライブラリのディレクトリ
  • Rakeを実行するディレクトリ

がある。

また、Rakeプラギンが1.7からrvmに対応したので、RakeプラギンをいれておくとJenkinsのシステム設定でrvmのパスを指定できるようになり、簡単にRakeのバージョンを切り替えられるようになっている。
Rakeタスクをいろいろ用意しておくことで、さまざまなクライアントの環境毎や、テストしたいページ毎に実行できる点が重要だ。
僕が現在開発しているプロジェクトでは次のようなタスクを用意している。

$ rake -T
rake all              # Run all selenium
rake ie6_test         # IE6 test
rake ie7_test         # IE7 test
rake ie8_test         # IE8 test
rake regression_test  # regression test
...

これらのタスクを登録しておけば、Jenkinsのjobが起動したときに各Rakeタスクを実行してくれる。テストの粒度をRakeタスクで調整できるので便利だと思う。
余談だが、JenkinsについてのtipsはJenkins Advent Calendar 2011に詰まっているので各エントリを一読しておこう。

リモート実行環境を整える

次に、selenium-webdriverをリモートの各クライアント環境でテストを実施する環境を整えよう。 selenium-webdriverの利点として、リモート実行環境の構築が簡単になることだ。 selenium-server-standalone.jarをダウンロードして、各テスト用クライアントマシンに配置する。 Windowsであれば、selenium-server-standalone.bat、Unix環境ならselenium-server-standalone.shを作成して、

java -jar selenium-server-standalone-*

という起動スクリプトを作成しておいて、クライアントマシンが起動するときにこの起動スクリプトを実行するように仕込んでおけばいつでもSeleniumテストをリモート実行できる。
# なぜ起動スクリプトを自動化しておく必要があるのかというと、クライアントマシンでテストを続けると、とくにWindowsだと重たくなったりするので定期的にクライアント自体を再起動するため

Chromeだけ別サーバが必要

Chromeのリモート実行はselenium-server-standaloneでは実行できないので、chromedriverを環境に応じてダウンロードしよう。zipファイルを展開すると実行ファイルが含まれており、それを実行するとサーバが起動する。
待ち受けるサーバのポート番号も異なるのでテストコードを次のように修正する。

@browser = Watir::Browser.new(:remote, :url => "http://192.168.1.175:9515", :desired_capabilities => :chrome)

まとめ

画面テスト書くことはともすれば単調作業になり面倒なものだけど、RSpecselenium-webdriverを使うことで簡潔かつ直観的に書けそこに面白みを見出させてくれる。
さらにJenkinsという強力なCI環境があるので、画面の自動テストも楽にできるようになった。
強力なツールを効率よく組み合わせ、テストをすることが、よりよいプロダクトを世に送り出すための下地なると思うので、実践していない人はぜひ導入してほしい。