SitePrismを使ってSelenium+Capybaraのテストをぺージオブジェクトパターンで書く

SitePrismを使ってSelenium+Capybaraのテストをぺージオブジェクトパターンで書く

スポンサーリンク

ウェブサイトのテストプログラムは、Page Object Modelで書くのが良いとされている。

SitePrismCapybaraを使うことで、画面をコード化する部分と画面を操作する部分を綺麗に分けてプログラムコードにすることができる。その結果、変化に強いテストプログラムを作ることが可能になる。

test

ぺージオブジェクトパターンでないテストプログラム

Seleniumを使ってWebサイトのテストコードを書くのテスト対象は、ページ数が少なく、かつ、シンプルなページのため、Page Object Modelにする良さがわかりにくい。そこで、複数のページ遷移を確認するテストを用意した。Yahoo! JAPANから三重県津市の天気のページへ移動できるかというテストだ。

#! ruby
# -*- mode:ruby; coding:utf-8 -*-

require 'capybara/rspec'
require 'selenium-webdriver'

RSpec.configure do |config|
  config.include Capybara::DSL
end

Capybara.default_driver = :selenium
Capybara.app_host = 'http://www.yahoo.co.jp/'

describe "Yahoo Weather" do

  before do
    visit '/'
  end
  
  it "Tsu weather" do
    # トップページを表示しているか確認
    expect(page).to have_content("主なサービス")
    click_on '天気'
    # 天気のページに移動したかを確認
    expect(page).to have_content("全国の天気")
    within('#adrssrch') do
      fill_in "p", with: '津'
      click_button "検索"
    end
    # 検索結果のページかを確認
    expect(page).to have_content("三重県津市")
  end
end

これも大したことないが、

  1. 意図したページに移動しているかを、have_content()を使ってページ中の文言で判断している
  2. 検索フォームに「津」を入力する際、within()を使ってページの構造を辿って入力エリアを決めている

という問題がある。

ページ中の文言で判断する問題

もし意図しないページに移動していても、そのページに同じ文言が存在していると、テストをパスしてしまう。そのページ固有の(ユニークな)文言が存在するのであれば、判断に使えるだろう。だが、そういった文言は普通ない。URLを使えば、単純なページ遷移のチェックには十分だろう。
セッション情報によって、同じURLであっても表示内容が異なるというのはよくあることなので、決して万能な解決策ではないが。

ページの構造を辿る問題

「三重県津市の天気のページに移動できるか?」というテストがページ構造に影響されるというのは、テストとしてどうであろう?テストの目的からは外れているのではないか?adrssrchというidを持つ要素が存在しているかどうかは、「ページのテスト」で行うべき項目であって、「ページの移動のテスト」で行うテスト項目ではない。「ついでにテストできる」という意見もあるが、それは、テストの管理が煩雑になって漏れを生む原因である。

ぺージオブジェクトパターンで書き直す

SitePrismを使って、このテストをPage Object Modelに書き直してみる。

#! ruby
# -*- mode:ruby; coding:utf-8 -*-
#
# Page Object Model version of test2_spec.rb

require 'capybara/rspec'
require 'selenium-webdriver'
require 'site_prism'

Dir[File.join(File.dirname(__FILE__), "*_page.rb")].each{ |f| require f }

RSpec.configure do |config|
  config.include Capybara::DSL
end

Capybara.default_driver = :selenium
Capybara.app_host = 'http://www.yahoo.co.jp/'

describe "Yahoo Weather" do

  before do
    @top = TopPage.new
    @top.load
  end
  
  it "Tsu weather" do
    # トップページを表示しているか確認
    expect(@top).to be_displayed
    weather_page = @top.click_weather
    # 天気のページに移動したかを確認
    expect(weather_page).to be_displayed
    result_page = weather_page.weather_search('津')
    # 検索結果のページかを確認
    expect(result_page).to be_displayed(loc: '津')
#    expect(page).to have_content("三重県津市")
  end
end


続いて、各ページの要素を定義するコード。上の書き直したコードと同じディレクトリに配置する。
Topページ

#! ruby
# -*- mode:ruby; coding:utf-8 -*-

# トップページ
class TopPage < SitePrism::Page
  set_url "/"

  # ページ中の要素を定義する
  element :weather_service, '#yahooservice > ul > li:nth-child(6) > a'


  # 要素に対する操作を定義する
  def click_weather
    weather_service.click
    # ページ遷移が発生する場合、新しいPage Objectを返す
    WeatherPage.new
  end
end


天気予報のページ。

#! ruby
# -*- mode:ruby; coding:utf-8 -*-

# 天気・災害ページ
class WeatherPage < SitePrism::Page
  set_url "http://weather.yahoo.co.jp/weather/"

  # ページ中の要素を定義する
  element :weather_search_field, '#searchText'
  element :search_button, '#yjw_button_search'

  # 要素に対する操作を定義する
  def weather_search(param)
    weather_search_field.set param
    search_button.click
    WeatherSearchResultPage.new
  end
end


検索結果のページ。

#! ruby
# -*- mode:ruby; coding:utf-8 -*-

# 天気・災害ページ-検索結果
class WeatherSearchResultPage < SitePrism::Page
  set_url "http://weather.yahoo.co.jp/weather/search/?p={loc}"

  element :search_word, '#main > div.yjw_main_md.yjw_clr > div.serch-keyword-frame01 > div > strong'
end

最後にGemfileの修正。SitePrismを使うよう gem ‘site_prism’ の1行を追加して、bundle installしておく。

# frozen_string_literal: true
# A sample Gemfile
source "https://rubygems.org"

# gem "rails"
gem "rspec"
gem "selenium-webdriver"
gem "capybara"

gem "site_prism"


まずはこのテストを bundle exec rspec test3_rspec.rb として実行してみよう。ページのコードではなく、ぺージオブジェクトパターンで書き直したテストコードを実行する。

テスト全体で見ると、行数は増えている。しかし、テスト内容をコードにした部分とページに関する内容をコードにした部分が分離できているため、テスト内容が変わらない限り、テストコードを変更する必要はない。

ページの移動の確認

expect().to be_displayed を使ってURLで確認するため、ページ中の文言に依存しない。URLのパラメータを渡すこともできるため、パラメータまで含めて確認することができる。

ページ構造の変更への追従

基本的に、「ページのデザインが変わった場合はページに関するコードを修正する」ことで対応できる。テストコードへの影響は生じない。

まとめ

Seleniumデザインパターン&ベストプラクティスには、Page Objectパターンで作っていく過程が書かれている。しかし、ページの各要素に対して行いたい基本的な操作は決まっているので、既存のライブラリを使う方がよいだろう。SitePrismは、そういったものの1つである。

スポンサーリンク

シェアする

  • このエントリーをはてなブックマークに追加
  • Evernoteに保存Evernoteに保存

フォローする

この記事へのコメント

Loading Facebook Comments ...

No Trackbacks.