PerlからSeleniumを利用してみた


こんにちは、2014年入社の野際です。
もうすぐ入社して一年経つわけですが、まだまだ学ぶことがたくさんあって楽しいです。

さて、今回初めて技術系の記事を書くことになりました。  
そこで、WEB+DB vol.85のSeleniumの記事を読んで、PerlSeleniumを触ってみたときのことを記事にしたいと思います。 
 

Seleniumとは

まずはじめにSeleniumとはなんぞや!?

SeleniumはE2Eテストを行うためのツールです。ユニットテストではテストしきれないフロントエンドのテストを行うのに効果的ですね。  
実際にブラウザが操作されている様子を確認できるので、実際にユーザが利用するときの流れに近いテストが可能です。


アプリケーションの準備

 今回は以下のような簡単なフォーム画面を使ってE2Eテストを行いたいと思います。  
本来はJavaScriptが絡んでくるような、複雑な画面でこそ真価を発揮するものなんですが・・実験的なものなので、まあよしとします。

開発環境はVMWare上のCentOSで、アプリケーション部分はCatalyst(Perl)を使って実装しました。

すべてのパラメータが送信されていれば、フォームの下にメッセージを表示するといった感じです。

 
送信前 

not-submitted


送信後

submitted
HTML
  
    
    SeleniumTest
  
  
    

SeleniumTest



[% IF user %][% user.name | html %]さんの年齢は[% user.age | html %]才です。[% END %]


 Seleniumの準備

Seleniumを使うためにはSelenium Serverというものが必要になります。

Selenium Server

また、Selenium ServerはデフォルトでFire Foxを使用するため、Chromeなど別のブラウザを使用するためには、専用のドライバをダウンロードする必要があります。  
僕はChromeを使っているので、以下のChrome Driverをダウンロードしました。

Selenium ServerとChrome Driverを適当なフォルダにダウンロードしたら、Selenium Serverを起動します。  
以下がSeleniumサーバの実行コマンドになります。Chromeを使用する場合は"-Dwebdriver.chrome.driver"にChromeDriverのパスを指定してください。
 
java -Dwebdriver.chrome.driver="chromedriver.exe" -jar selenium-server-standalone-2.44.0.jar


10:57:55.642 INFO - Launching a standalone server
10:57:56.109 INFO - Java: Oracle Corporation 24.71-b01
10:57:56.109 INFO - OS: Windows 7 6.1 amd64
10:57:56.193 INFO - v2.44.0, with Core v2.44.0. Built from revision 76d78cf
10:57:56.637 INFO - RemoteWebDriver instances should connect to: http://127.0.0.1:4444/wd/hub
10:57:56.639 INFO - Version Jetty/5.1.x
10:57:56.639 INFO - Started HttpContext[/selenium-server/driver,/selenium-server/driver]
10:57:56.639 INFO - Started HttpContext[/selenium-server,/selenium-server]
10:57:56.639 INFO - Started HttpContext[/,/]
10:57:57.293 INFO - Started org.openqa.jetty.jetty.servlet.ServletHandler@7e0ef515
10:57:57.293 INFO - Started HttpContext[/wd,/wd]
10:57:57.314 INFO - Started SocketListener on 0.0.0.0:4444
10:57:57.314 INFO - Started org.openqa.jetty.jetty.Server@113dbe08

お、なんか無事に起動したっぽいですね。ここでアクセスログを確認できるので別ペインで開いておくといいと思います。

Selenium::Remote::Driverの準備

cpanm Selenium::Remote::Driver

さっそく簡単な起動テストしましょう。

Perl

use strict;
use warnings;

use utf8;

use Selenium::Remote::Driver;

my $driver = Selenium::Remote::Driver->new( 'browser_name' => 'chrome' ); # ブラウザの起動

$driver->get('http://www.google.com');
print $driver->get_title();

$driver->quit(); # ブラウザの終了

出力

Google

上記のソースコードは、ブラウザを起動してGoogleのタイトルを出力し、ブラウザを終了します。  
このように、ブラウザの起動→処理→ブラウザの終了が一連の流れになります。  

先に、後ほど使うメソッドを簡単に紹介しておきます。本当はもっとあるんですがここでは書ききれませんでした。


Selenium::Remote::Driver->set_implict_wait_timeout( string $milliseconds )

要素の検索時にタイムアウトする時間を設定する。  
組み込みのsleep関数とかと違って、要素が見つかればが処理が再開されます。

Selenium::Remote::Driver->get( string $url )

GETリクエストを送信する。

Selenium::Remote::Driver->find_element( string $target, string? $schema )

要素を検索する。$schemaには"id"や"class","css","xpath"などが指定できます。  
デフォルトは"xpath"ですが、ゆとりな僕はxpathを使ったことがありません。  
ちなみにCSSが推奨らしいです。

Selenium::Remote::WebElement->send_keys( string @strings )

要素にキーを送信する。  
フォームに値を入力するのに使います。

Selenium::Remote::WebElement->click()

 要素をクリックする。


テストの実施

さて、ここまでで結構長くなってしまいましたがいよいよテストを実施します。

テストコードはTest::Classモジュールを使って書きました。

パラメータが送られたときのメッセージと、リセットが押されたときの挙動をチェックしています。

Perl 

package SeleniumTest;

use strict;
use warnings;

use utf8;

use parent 'Test::Class';

use Test::More;

use Selenium::Remote::Driver;

use constant NAME => 'noge';
use constant AGE  => 23;

SeleniumTest->runtests;

sub startup : Test( startup ) {
  my ( $self ) = @_;

  $self->{ driver } = Selenium::Remote::Driver->new(
      'browser_name' => 'chrome',
      );
  $self->{ driver }->set_implicit_wait_timeout( 5 );

  return;
}

sub setup : Test( setup ) {
  my ( $self ) = @_;

  $self->{ driver }->get( 'http://192.168.60.168:3003/selenium/' );

  $self->{ text_name } = $self->{ driver }->find_element( 'input[name=name]'  , 'css' );
  $self->{ text_age }  = $self->{ driver }->find_element( 'input[name=age]'   , 'css' );
  $self->{ submit }    = $self->{ driver }->find_element( 'input[type=submit]', 'css' );
  $self->{ reset }     = $self->{ driver }->find_element( 'input[type=reset]' , 'css' );

  return;
}

sub t001_submit_name_and_age : Tests {
  my ( $self ) = @_;

  $self->{ text_name }->send_keys( NAME );
  $self->{ text_age }->send_keys( AGE );
  $self->{ submit }->click;

  is( $self->{ driver }->find_element( 'p[class=message]', 'css' )->get_text, NAME . 'さんの年齢は' .
AGE . '才です。' );

  return;
}

sub t002_submit_name : Tests {
  my ( $self ) = @_;

  $self->{ text_name }->send_keys( NAME );
  $self->{ submit }->click;

  is( $self->{ driver }->find_element( 'p[class=message]', 'css' )->get_text, '' );

  return;
}

sub t003_submit_age : Tests {
  my ( $self ) = @_;

  $self->{ text_age }->send_keys( AGE );
  $self->{ submit }->click;

  is( $self->{ driver }->find_element( 'p[class=message]', 'css' )->get_text, '' );

  return;
}

sub t004_reset : Tests {
  my ( $self ) = @_;

  $self->{ text_name }->send_keys( NAME );
  $self->{ text_age }->send_keys( AGE );
  $self->{ reset }->click;

  is( $self->{ text_name }->get_text, '' );
  is( $self->{ text_age }->get_text, '' );

  return;
}

sub shutdown : Test( shutdown ) {
  my ( $self ) = @_;

  $self->{ driver }->quit;

  return;
}

出力

ok 1 - t001 submit name and age
ok 2 - t002 submit name
ok 3 - t003 submit age
ok 4 - t004 reset
ok 5 - t004 reset
1..5

無事に成功しました!!  
本当はテスト結果をGIFアニメにしたかったんですが、終了が速過ぎて捉えきれませんでした・・

とにかく早いんで見ていて爽快です。是非お試しください!!