最近開発をしていて、入力フォームが沢山ある案件で、あれを修正するとあっちの画面にもこっちの画面に影響する、といった現象が連鎖してしまい、段々と影響範囲をテストできなくなってきました。これを何とかするため、ついにSelenium WebDriverを使うようになりました。
このseleniumですが、ローカルで自分のマシンで起動する分にはブラウザがインストールされているので普通に起動できます。
しかしlinux等のXウインドウを起動していないコンソールのみのサーバだと、ブラウザはインストールされていないし、そもそも画面を表示できないためseleniumは起動しません。
本当ならwindowsサーバをAWS等で導入するのが最善ですが、お金がかかるのが嫌です。そこで前述のlinuxサーバでseleniumを起動する、xvfbを使ったヘッドレス(画面が無い)にseleniumを起動する方法を調べました。
手順
Xvfbについて
画面を起動できないCentOSで、Xvfbを使って仮想ディスプレイをエミュレートし、seleniumを実行します。
Xvfb はディスプレイのないサーバ機などで X Window System の画面入出力をシミュレートする X11 のサーバ・ソフトウェアである。 例えば,あるサイトのリンクにそのページ画面のサムネイル画像を掲げているのをよく目にするが,これをサーバで自動的にやろうとすると,JavaServlet などでサイトのページを取得し,ブラウザと同様にレンダリングし,画面イメージを生成するわけだ。 このとき Awt などの「ディスプレイ」の存在を仮定するライブラリを使うのが常である。 そこで,ディスプレイ・レスの UNIX サーバ機でもそれをエミュレートする,仮想フレームバッファなるインタフェースを提供するのが Xvfb である。
http://yasuda.homeip.net/insomnia/2010/04/xvfb-install.html
環境
今回はCentOS6.5で試しています。
インストールするもの
まずjdkをインストールします。
sudo yum -y install java-1.7.0-openjdk.x86_64 java-1.7.0-openjdk-devel.x86_64
続いてXvfbとfirefoxをインストールします。
sudo yum -y install xorg-x11-server-Xvfb firefox
このままだとブラウザが文字化けするので、日本語フォントをインストールします。
sudo yum -y groupinstall "Japanese Support"
設定
vi ~/.bash_profile # .bash_profile # Get the aliases and functions if [ -f ~/.bashrc ]; then . ~/.bashrc fi # User specific environment and startup programs export DISPLAY=:1 PATH=$PATH:$HOME/bin export PATH
「export DISPLAY=:1」が必要な設定となります。
Xvfbを起動
以下のようにダラダラコンソールが流れますが、これで無事起動します。
なお、&でバックグラウンド実行して下さい。
[vagrant@localhost selenium-test]$ Xvfb :1 -screen 0 1024x768x24 & [vagrant@localhost selenium-test]$ _XSERVTransmkdir: Owner of /tmp/.X11-unix should be set to root Initializing built-in extension Generic Event Extension Initializing built-in extension SHAPE Initializing built-in extension MIT-SHM Initializing built-in extension XInputExtension Initializing built-in extension XTEST Initializing built-in extension BIG-REQUESTS Initializing built-in extension SYNC Initializing built-in extension XKEYBOARD Initializing built-in extension XC-MISC Initializing built-in extension SECURITY Initializing built-in extension XINERAMA Initializing built-in extension XFIXES Initializing built-in extension RENDER Initializing built-in extension RANDR Initializing built-in extension COMPOSITE Initializing built-in extension DAMAGE Initializing built-in extension MIT-SCREEN-SAVER Initializing built-in extension DOUBLE-BUFFER Initializing built-in extension RECORD Initializing built-in extension DPMS Initializing built-in extension X-Resource Initializing built-in extension XVideo Initializing built-in extension XVideo-MotionCompensation Initializing built-in extension SELinux Initializing built-in extension GLX
firefoxを起動
firefoxも起動時にダラダラコンソールに流れますが、これで無事に起動しています。
[vagrant@localhost selenium-test]$ firefox & [vagrant@localhost selenium-test]$ 5 XSELINUXs still allocated at reset SCREEN: 0 objects of 232 bytes = 0 total bytes 0 private allocs DEVICE: 0 objects of 96 bytes = 0 total bytes 0 private allocs CLIENT: 0 objects of 152 bytes = 0 total bytes 0 private allocs WINDOW: 0 objects of 40 bytes = 0 total bytes 0 private allocs PIXMAP: 1 objects of 16 bytes = 16 total bytes 0 private allocs GC: 4 objects of 16 bytes = 64 total bytes 0 private allocs CURSOR: 0 objects of 8 bytes = 0 total bytes 0 private allocs DBE_WINDOW: 0 objects of 24 bytes = 0 total bytes 0 private allocs TOTAL: 5 objects, 80 bytes, 0 allocs 1 PIXMAPs still allocated at reset PIXMAP: 1 objects of 16 bytes = 16 total bytes 0 private allocs GC: 4 objects of 16 bytes = 64 total bytes 0 private allocs CURSOR: 0 objects of 8 bytes = 0 total bytes 0 private allocs DBE_WINDOW: 0 objects of 24 bytes = 0 total bytes 0 private allocs TOTAL: 5 objects, 80 bytes, 0 allocs 4 GCs still allocated at reset GC: 4 objects of 16 bytes = 64 total bytes 0 private allocs CURSOR: 0 objects of 8 bytes = 0 total bytes 0 private allocs DBE_WINDOW: 0 objects of 24 bytes = 0 total bytes 0 private allocs TOTAL: 4 objects, 64 bytes, 0 allocs 6 XSELINUXs still allocated at reset SCREEN: 0 objects of 232 bytes = 0 total bytes 0 private allocs DEVICE: 0 objects of 96 bytes = 0 total bytes 0 private allocs CLIENT: 0 objects of 152 bytes = 0 total bytes 0 private allocs WINDOW: 0 objects of 40 bytes = 0 total bytes 0 private allocs PIXMAP: 2 objects of 16 bytes = 32 total bytes 0 private allocs GC: 4 objects of 16 bytes = 64 total bytes 0 private allocs CURSOR: 0 objects of 8 bytes = 0 total bytes 0 private allocs DBE_WINDOW: 0 objects of 24 bytes = 0 total bytes 0 private allocs TOTAL: 6 objects, 96 bytes, 0 allocs 2 PIXMAPs still allocated at reset PIXMAP: 2 objects of 16 bytes = 32 total bytes 0 private allocs GC: 4 objects of 16 bytes = 64 total bytes 0 private allocs CURSOR: 0 objects of 8 bytes = 0 total bytes 0 private allocs DBE_WINDOW: 0 objects of 24 bytes = 0 total bytes 0 private allocs TOTAL: 6 objects, 96 bytes, 0 allocs 4 GCs still allocated at reset GC: 4 objects of 16 bytes = 64 total bytes 0 private allocs CURSOR: 0 objects of 8 bytes = 0 total bytes 0 private allocs DBE_WINDOW: 0 objects of 24 bytes = 0 total bytes 0 private allocs TOTAL: 4 objects, 64 bytes, 0 allocs
seleniumを起動する
これでこのサーバに対してseleniumを実行すると、画面が無いのにseleniumが起動でき、スクリーンショットを取ることもできます。
制限
今のところFirefxしか起動しません。
IEはそもそもWindowsOSでしか起動しません。
chromeは・・・できるんでしょうかね。未調査です。
おまけ
不要かと思いますが、seleniumのソースコードのサンプルも載せてみます。
build.gradle
gradleを使ってユニットテストを実行します。SLF4jは余計ですが入れてます。
apply plugin: 'java' apply plugin: 'eclipse' sourceCompatibility = 1.7 [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' repositories { mavenCentral() } dependencies { // util compile group: 'com.google.guava', name: 'guava', version: '17.0' compile group: 'joda-time', name: 'joda-time', version: '2.3' // log compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2' compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.7' compile group: 'org.slf4j', name: 'jcl-over-slf4j', version: '1.7.7' compile group: 'org.slf4j', name: 'jul-to-slf4j', version: '1.7.7' compile group: 'org.slf4j', name: 'log4j-over-slf4j', version: '1.7.7' // junit testCompile group: 'junit', name: 'junit', version: '4.11' // selenium testCompile group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '2.43.1' testCompile group: 'com.vividsolutions', name: 'jts', version: '1.13' }
テストクラス
普通にJUnitからseleniumを実行し、yahooとトップのスクリーンキャプチャを保存して終了します。
package test.selenium; import java.io.File; import java.io.IOException; import java.util.concurrent.TimeUnit; import org.joda.time.DateTime; import org.junit.Test; import org.openqa.selenium.OutputType; import org.openqa.selenium.TakesScreenshot; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; import com.google.common.io.Files; public class SeleniumTest { @Test public void testSelenium() { // ドライバを生成 WebDriver driver = new FirefoxDriver(); // タイムアウトを設定 driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS); // ウインドウを最大化 driver.manage().window().maximize(); // 画面遷移 driver.get("http://www.yahoo.co.jp"); // yahooのスクリーンショットを/tmpに保存 File srcFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE); File destFile = new File("/tmp/" + DateTime.now().toString("yyyyMMddHHmmss") + ".png"); try { Files.move(srcFile, destFile); } catch (IOException e) { throw new RuntimeException(e); } // ドライバを終了 driver.quit(); } }
gradleからテストしてみる
vagrantで適当に環境を作ったので、パスは適当ですが、以下のように実行すると、/tmpにスクリーンショットが保存されます!
[vagrant@localhost selenium-test]$ /tmp/gradle-2.1/bin/gradle clean test :clean :compileJava UP-TO-DATE :processResources :classes :compileTestJava :processTestResources UP-TO-DATE :testClasses > Bu:test BUILD SUCCESSFUL Total time: 2 mins 17.79 secs
雑感
selenium、動かすまでが大変だと思ったけど、案外簡単だなー
これでseleniumによる自動結合テストなんかができるようになるぞ。
jenkinsとかCircleCIとかからseleniumを呼ぶといいぞ。
しかしいざ結合テストクラス作ってみると、粒度に迷うんだよな〜
網羅するようなテスト書くとメンテできなくなるからな〜
そうだな。網羅系のテストにせず、重要な画面の疎通確認程度の粒度から始めるといいかもな。
ちなみにseleniumからhtmlタグも取得できるから、noindexになってるかとか、SEOのテストも一緒にできるぞ!
ふむふむ。凄いなあ。
あ、でもたまにdevelop環境の画面に本番環境のリンクがあって本番環境に遷移しちゃって、誤って本番環境でお問い合わせして、実際のお店にメールが送られて怒られる事もありそう・・・
お、いいところに気づいたな。その通りだ。
そういう事故が起きる可能性を考慮して、画面のURLを取得して、テストしていいURLでない場合はテスト失敗、等と保険をうっておいた方がいいな。
やるお「これでバグが減るといいな」
やらないお「これってテスターさんの存在意義が薄れるよな。テスターさん大ピンチだ」