redmine のテストを Selenium2.0 WebDriver を使って firefox と IE の2つのブラウザでやってみる

概要

某ベンダ系アジャイルコミュニティの合宿で Selenium を使ったテストをやってみたので、
復習がてら Windows XP SP3上でタイトルのことをやってみたりする。

テストの内容
  • ログイン
  • チケット作成
  • チケット一覧で作成したチケットを確認
  • ログアウト

まで。

構成

redmine <--> firefox(IE) <--> selenium(webdriver) <--> test::unit
こんな感じ?
selenium と webdriver のところが怪しい。

なぜ Selenium(WebDriver) なの?

なんで WebDriver なの?という説明が Selenium 2.0 と WebDriver — Selenium 日本語ドキュメント にあった。

  • Mult-browser testing including improved functionality for browsers not well-supported by Selenium-1.0.
  • Handling multiple frames, multiple browser windows, popups, and alerts.
  • Page navigation.
  • Drag-and-drop.
  • AJAX-based UI elements.
  • 複数のブラウザを使ったテスト
  • ドラッグ&ドロップのテスト
  • AJAX 使ったところのテスト
  • 複数のフレーム、ウィンドウ、ポップアップ、アラートのテスト
  • Page navigation.(ってふつーにできそうだけど何かあるのかな?)

準備物

Redmine 陣営

各自、適当にインストールする。
selenium-webdriver と cucumber を 1.8.7 に同居させられなかったので Selenium 陣営は 1.9.2 を選択…。

プロジェクト "Walpurgis Night" と ユーザ "qb" をあらかじめ作っておく。
諸般の事情により、とりあえず英語。

操作を記録して、Test::Unit ファイルに吐き出す

firefox -> ツール -> Selenium IDE を選択。
以下の操作を行う。赤い●のボタンを押すと記録開始、も一回押すと記録終了。

  • ログイン
  • チケット作成
  • チケット一覧を表示
  • ログアウト
記録した操作を実行して動くか確かめる

メニュー -> アクション -> 現在のテストケースを実行。

念のため、記録した操作を保存する

メニュー -> テストケースを保存。

記録した操作を Test::Unit に吐き出す

これは便利だなぁ!
メニュー -> テストケースをエクスポート -> Ruby Test::Unit(WebDriver)
ここで、 テストスイート にしちゃうとエラーが出るので注意。

Test::Unit を使って保存した操作を再現する

1.9のおまじないと、rubygems をインポートする
# -*- encoding: utf-8 -*-
require "rubygems"
タイムアウト?の時間を2秒にする
  @driver.manage.timeouts.implicit_wait = 2
URLを変更する(何のためって言えばいいのだろう)
  @driver.get "http://127.0.0.1:3000"
Option を選択するところは自動出力してくれないので自力で書く

select - How do I set a an option as selected using selenium-webdriver (selenium 2.0) client in ruby - Stack Overflow を参考に。
メソッドに抜き出しておいてもよさそう。

実行する

無事チケットができてたらOK

ruby \path\to\testcase.rb

結果を検証する

ちゃんと希望通りの内容になっているのか検証をする。
手抜きをして、

  • チケット一覧で目的のタイトルのチケットがあること
    • 該当するチケットのトラッカー、優先度、担当者が期待通りであること
    • チケット番号は動的に変わってしまうからチェックなし

のみ検証してみる。

検証するために必要な操作
  • チケット一覧(テーブル)を見つける
  • 見つけたテーブルの一番上にあるデータを取り出す
  • タイトル、トラッカー、優先度、担当者の値を取り出し、チェックする

ができないといけない。ふぅ。
ここで Web Developer の出番な訳ですよ!

Web Driver を使って要素の情報を取り出す

使い方は(後で書くかも)
情報 -> 要素の情報を表示する が使えると思う。

テーブルを見つけて値を取り出し、検証する

チケット一覧テーブルの一番上にある行を取り出せば良い感じ。
トラッカーの内容が Feature であることを確認するにはこんな感じ。

  @driver.find_element(:xpath, "//table[@class='list issues']/tbody/tr[1]")
  assert_equal "Feature", latest_issue.find_element(:class, "tracker").text, "tracker"
実行して確かめる
> ruby \path\to\testcase.rb
Loaded suite Redmine-WalpurgisNight
Started
.
Finished in 16.453125 seconds.

1 tests, 6 assertions, 0 failures, 0 errors, 0 skips

Test run options: --seed 31656

できた!!

firefox だけじゃなくって IE でも動かしてみる

これができてこそ。setup の最初の行をちょいちょいと書き換える

@driver = Selenium::WebDriver.for :ie

ログインする時にパスワードをうんぬんのダイアログが出て最初は失敗したけど、
2回目は成功。
すげーすげー。

でも、いちいちここ切り替えるの面倒。
上手い指定の方法が思いつかないので、環境変数使うことにする。
上手く切り替わったゾ!

ここまでのコード

今度は Cucumber と組み合わせてできるかやってみよう。

# -*- encoding: utf-8 -*-
require "rubygems"
require "selenium-webdriver"
require "test/unit"

class RedmineWalpurgisNight < Test::Unit::TestCase

  def setup
    raise "Please set environment variable 'WEBDRIVER_TYPE' (ex. firefox, ie)" unless ENV["webdriver_type"]
    @driver = Selenium::WebDriver.for ENV["webdriver_type"].to_sym
    @driver.manage.timeouts.implicit_wait = 2
    @verification_errors = []
  end
  
  def teardown
    return unless @driver
    @driver.quit
    assert_equal [], @verification_errors
  end
  
  def test_redmine_walpurgis_night
    @driver.get "http://127.0.0.1:3000"
    @driver.find_element(:link, "ログイン").click
    @driver.find_element(:id, "username").clear
    @driver.find_element(:id, "username").send_keys "qb"
    @driver.find_element(:id, "password").clear
    @driver.find_element(:id, "password").send_keys "homuhomu"
    @driver.find_element(:name, "login").click
	select_option @driver.find_element(:css,'select'), "Walpurgis Night"
    @driver.find_element(:link, "New issue").click
	select_option @driver.find_element(:id,'issue_tracker_id'), "Feature"
    @driver.find_element(:id, "issue_subject").clear
    @driver.find_element(:id, "issue_subject").send_keys "As Incubator, I want magical girl."
    @driver.find_element(:id, "issue_description").clear
    @driver.find_element(:id, "issue_description").send_keys "foobaa"
	select_option @driver.find_element(:id,'issue_priority_id'), "High"
	select_option @driver.find_element(:id,'issue_assigned_to_id'), "qb incubator"
    @driver.find_element(:name, "commit").click
    @driver.find_element(:link, "Issues").click

	latest_issue = @driver.find_element(:xpath, "//table[@class='list issues']/tbody/tr[1]")
	assert_equal "Feature", latest_issue.find_element(:class, "tracker").text, "tracker"
	assert_equal "New", latest_issue.find_element(:class, "status").text, "status"
	assert_equal "High", latest_issue.find_element(:class, "priority").text, "priority"
	assert_equal "As Incubator, I want magical girl.", latest_issue.find_element(:class, "subject").text, "subject"
	assert_equal "qb incubator", latest_issue.find_element(:class, "assigned_to").text, "assignee"
	
    @driver.find_element(:link, "Sign out").click
  end
  
  def element_present?(how, what)
    @driver.find_element(how, what)
    true
  rescue Selenium::WebDriver::Error::NoSuchElementError
    false
  end
  
  def select_option(my_select, option_text)
	my_select.find_elements( :tag_name => "option" ).find do |option|
      option.text == option_text
    end.click
  end
  
  def verify(&blk)
    yield
  rescue Test::Unit::AssertionFailedError => ex
    @verification_errors << ex
  end
end