Ruby
Capybara
selenium-webdriver

CapybaraとSeleniumを使ってPageObjectデザインパターンによる画面変更に強いUIテストのサンプル

More than 3 years have passed since last update.

概要

画面操作とテストシナリオが疎結合にできるPageObjectデザインパターンを試したかったので、検索ワードにヒットする商品を自動購入するAmazonの自動購入処理を書きました。
参考ページをCapybaraに移植したものになります。

PageObjectデザインパターンとは

公式によるとPageObjectデザインパターンとは、以下だそうです。

・The public methods represent the services that the page offers(publicメソッドは、ページが提供するサービスを表す)
・Try not to expose the the internals of the page (ページの内部を公開しないこと)
・Generally don’t make assertions (原則としてassertionを行わないこと)
・Methods return other PageObjects (メソッドは他のPageObjectsを返す)
・Need not represent an entire page (ページ全体を表す必要はない)
・Different results for the same action are modelled as different as different methods (同じアクションに対して異なる結果となる場合には異なるメソッドとしてモデル化する)

PageObjectデザインパターンを利用して画面変更に強いUIテストを作成する│ソフトウェアテストラボ

仕様

Amazonのホームから、任意の検索ワードで検索して、商品をカゴに入れて代引きで購入します。

実装

require "capybara"
require "selenium-webdriver"

Capybara.current_driver = :selenium

Capybara.configure do |config|
  config.run_server = false
  config.app_host   = "http://www.amazon.co.jp"
end

id = "example@exmaple.com"
pass = "password"

module Amazon
  class Page
    include Capybara::DSL
  end

  class HomePage < Page
    def goHome
      visit "/"
      self
    end

    def searchItem word
      fill_in "field-keywords" , with: word
      click_on '検索'
      SearchResultPage.new
    end
  end

  class SearchResultPage < Page
    def choiceItem num=0
      all(".s-item-container")[num]
        .find(".a-link-normal.s-access-detail-page.a-text-normal").click
      current_window.close
      switch_to_window windows[0]
      ItemDetailPage.new
    end
  end

  class ItemDetailPage < Page
    def addToShoppingCart
      sleep 2
      all("[id='add-to-cart-button']")[0].click
      RecommendPage.new
    end
  end

  class RecommendPage < Page
    def goCart
      find("#hlb-ptc-btn-native").click
      LoginPage.new
    end
  end

  class LoginPage < Page
    def login id, password
      fill_in 'email' , with: id
      fill_in 'password' , with: password
      find("#signInSubmit-input").click
      DeliveryPage.new
    end
  end

  class DeliveryPage < Page
    def choiceAddress num=0
      all("a.a-declarative.a-button-text")[num].click
      sleep 3
      DeliveryOptionPage.new
    end
  end

  class DeliveryOptionPage < Page
    def goNextPage
      first(".a-button-text").click
      sleep 3
      PaymentPage.new
    end
  end

  class PaymentPage < Page
    def goNextPage
      first(".a-button-text").click
      sleep 2
      ConfirmationPage.new
    end

    def changeOption option
      if option == :cod
        first("div.radio-col.spacing-right-small > input[value='cashOnDeliveryCash']").set(true)
      end
      self
   end
  end

  class ConfirmationPage < Page
    def decision
      save_screenshot('screenshot.png')
      first(".a-button-text.place-your-order-button").click
    end
  end

end


Amazon::HomePage.new
  .goHome
  .searchItem("Selenium")
  .choiceItem
  .addToShoppingCart
  .goCart
  .login(id, pass)
  .choiceAddress(0)
  .goNextPage
  .changeOption(:cod)
  .goNextPage
  .decision

まとめ

スクレイピングするのが好きなのですが、ページオブジェクトパターンは返り値としてページオブジェクトを使うのでルールに準拠するとスクレイピングには使えないですね。
ほどよく、UI操作とシナリオを分割してコード書きたいです。

参考文献

注意

Kindleの商品の場合、カートに入れるボタンが無いなど、一部動かない場合があります。
サンプルということで。
住所はすでに登録されているものとして、進めています。