freee Engineers Advent Calendar 2016
12月17日担当の @futoase です。
現在、E2Eテスト基盤構築の担当をしています。1
Capybara、SitePrism および Selenium に触れていく中で自分や弊社メンバーから得た知見について記載します。
4点の内容となります。
- Capybara + SitePrism
- Selenium
- E2Eの目的
- E2E基盤構築を担当してみて思ったこと
Capybara + SitePrism
同僚の @kompiro が Capybara + SitePrism を使うことを提案、フレームワーク化を行ってくれたのでテストケース作成に利用しています。2
Selenium/Appium Advent Calendar 2016にてSitePrismを利用したPageObjectsパターンを使ったテスト作成について網羅的に説明した記事を書いています。
RubyでPageObjectsパターンを実装できる SitePrism のご紹介
例
例として弊社のランディングページに対して
Capybara + SitePrism によるPageObjects, specを書いています。
PageObjectsとテスト(spec)になります。3
PageObjects
- ログイン画面(secure.freee.co.jp)
require 'site_prism'
module Secure
class Login < SitePrism::Page
set_url 'https://secure.freee.co.jp/users/login/'
end
end
- LP画面(www.freee.co.jp)
require 'site_prism'
module WWW
# LP
class Index < SitePrism::Page
set_url '/'
element :login_link, 'div.ncms-mod-header1__login a'
end
end
- テスト
require 'spec_helper'
require 'pages/www/index'
require 'pages/secure/login'
feature 'www.freee.co.jp' do
context 'click of link for logged in' do
given(:index_page) { WWW::Index.new }
subject(:login_page) { Secure::Login.new }
scenario 'displayed the page of sigin up' do
index_page.load
expect(index_page).to be_displayed
index_page.login_link.click
expect(login_page).to be_displayed
end
end
end
E2Eテストの実行
rspecを実行するとブラウザ(Chrome)が立ち上がり、www.freee.co.jpへアクセスをします。
その後ログインボタンをCapybaraがAPIを経由しclickすることによりsecure.freee.co.jpに遷移し、ログインページに遷移しているのでテストが正常に終了します。4
> bundle ex rspec spec/index_spec.rb
要素の指定
Capybaraではデフォルトではcss selectorにより指定するようになっています。取得したい要素を調べるにははchrome devtoolsを使います。
ログインボタン要素の取得が確認できたら、PageObjectのelementにログインボタンのcss selectorを関連付けを記述します。
element :login_link, 'div.ncms-mod-header1__login a'
こうすることで、index_page.login_link
という形でログインボタン要素を利用することが可能になります。
index_page.login_link.click
にアクセスすれば、マウスクリックイベントが index_page.login_link
に対して発行されます。
xpath
SitePrismのelementヘルパに :xpath を渡すことで指定することが可能です。
element :login_link, :xpath "//div[@class='ncms-mod-header1__login']/a"
chrome devtoolsでは $x5 というヘルパ関数を利用するとxpathを指定し、DOM要素を取得することができます。
マウスクリック、キーアサイン
Capybaraで、clickや、キーアサインイベントを送る操作などのAPIを用意しています。6
マウスクリックであれば click、エンターキーを押す操作であれば send_keys(:enter) となります。
テストではログインボタンに対しマウスクリックを実行します。
index_page.login_link.click
DOM構築を待つ
非同期処理でfront側がbackend側に通信する仕組みが取り込まれている場合、サイトにアクセスした直後ではAPI問い合わせ中のため、DOMが構築されていない場合があります。その場合、CapybaraはDOM要素を見つけられないため、テストが実行できず、失敗状態となることがあります。7
SitePrismのelement要素で使えるwait_for_ ヘルパを利用すると解決します。
index_page.wait_for_login_link.click
index_page.login_link.click
"wait_for_#{element要素名}" と指定するだけで、elementのDOMが構築されるまで
Capybaraがテスト実行を一時的に停止してくれます。
デバッグ
pry
bundle ex rspec spec/index_spec.rb
From: spec/index_spec.rb @ line 14 :
9: given(:index_page) { WWW::Index.new }
10: subject(:login_page) { Secure::Login.new }
11:
12: scenario 'displayed the page of sigin up' do
13: index_page.load
=> 14: binding.pry
15: expect(index_page).to be_displayed
16:
17: index_page.login_link.click
18: expect(login_page).to be_displayed
19: end
指定している要素が見つからないときにテストを一時的にとめるために重宝します。
current_path
表示しているサイトのパスを確認したいときに利用する。
[1] pry > current_path
=> "/
PageObjectのset_url ヘルパ関数にて指定したURLが表示されます。
URLのパース
AnchorタグからURLの要素を抜き出し、変数に格納したい場合、
addressableが利用できます。8
[1] pry > url = "http://example.com/test#hoge?key=value"
=> "http://example.com/test#hoge?key=value"
[2] pry > parse_url = Addressable::URI.parse(url)
=> #<Addressable::URI:0x3fd6f6dd4c28 URI:http://example.com/test#hoge?key=value>
[3] pry > parse_url.fragment
=> "hoge?key=value"
[4] pry > parse_url.scheme
=> "http"
[5] pry > parse_url.host
=> "example.com"
成果物
テストフレームワークの成果物はGithubにあげました。
ぜひ参考にしてください。
Selenium
E2Eは性格上、テストの実行に時間がかかります。
テストを実行するプロセスを増やすアプローチを取り、並列化することで解消しようと試みています。9
Seleniumを利用してhub, nodeの基盤を構築することでスケールアウトする仕組みを作ることが可能です。
selenium hub, node
seleniumはhub, nodeモードがあります。10
- selenium hub
- selenium nodeと通信し、node側のブラウザ操作を管理します
- Capybaraではselenium hubをブラウザとして指定することができます
- selenium node
- hubとhttpを利用し(API)通信を行い、node上に同居しているブラウザをAPI経由で操作します
docker-selenium
selenium hub, selenium nodeについて環境を1から構築するのは苦労するので、
docker-seleniumという成果物を利用します。
成果物は以下のものがあります。firefox or Chromeがプリインストールされている環境が用意されています。11,12
- selenium/node-chrome
- selenium/node-firefox
- selenium/node-chrome-debug
- selenium/node-firefox-debug
- selenium/standalone-chrome
- selenium/standalone-firefox
- selenium/standalone-chrome-debug
- selenium/standalone-firefox-debug
試す
試してみるには、dockerの環境が必要です。また、docker-composeを利用します。13
docker-compose.ymlを用意します。14
version: "2"
services:
hub:
image: selenium/hub:3.0.1-carbon
ports:
- "4444:4444"
chrome:
image: selenium/node-chrome-debug:3.0.1-carbon
ports:
- "15900:5900"
depends_on:
- hub
environment:
- no_proxy=localhost
- HUB_PORT_4444_TCP_ADDR=hub
- HUB_PORT_4444_TCP_PORT=4444
- HUB_ENV_no_proxy=localhost
docker-composeによりdocker containerを立ち上げます。
> docker-compose up
selenium hub は、 http://localhost:4444/ にアクセスできます。
selenium nodeへの接続はFinderを立ち上げ、⌘+KでVNCサーバに接続することができます。
(docker-composeの設定で、localhost:15900に対して接続します)
passwordは"secret"と入力し、接続します
docker container上で立ち上がっている ubuntu 環境にアクセスできます。
selenium nodeに対してテストケースを実行するには
selenium hub経由でnodeに対してE2Eテストを実行することができます。
Capybaraの設定でselenium hubを利用するようにすることで可能になります。
Capybara.register_driver :selenium_chrome do |app|
Capybara::Selenium::Driver.new(
app,
browser: :remote,
desired_capabilities: Selenium::WebDriver::Remote::Capabilities.chrome,
url: 'http://localhost:4444/wd/hub'
)
end
Capybara.current_driver = :selenium_chrome
Selenium::WebDriverを利用するように切り替えるだけで、
Capybaraの利用するブラウザがSelenium hubに切り替わります。15
nodeを別ホストで運用するには
selenium nodeを別ホストで運用することも可能です。
ただし、docker-seleniumを利用してnodeを立ち上げるには注意点があります。
node自身がサーブレットにアクセスする必要があるため、5555ポートに対し自分自身(node)を指定する必要があります。そのためのオプション引数を適用するには、Issueに上がっているパッチの適用が必要(2016/12/17現在)になっています。
Assuming that the node is running on port 5555 all serlvets added to the node are accessible under http://xxx:5555/extra/ path.
以下のブログに掲載されていました。
SE_OPTSに対し、NICに割り当てられたIPアドレス、ポート番号を渡して立ち上げる形になります。
version: "2"
services:
chrome:
image: selenium/node-chrome-debug:3.0.1-carbon
ports:
- "15900:5900"
depends_on:
- hub
environment:
- no_proxy=localhost
- HUB_PORT_4444_TCP_ADDR={selenium hubが動作しているホストのIPアドレス}
- HUB_PORT_4444_TCP_PORT=4444
- SE_OPTS=-host {selenium nodeのNICに割り当てられたIPアドレス} -port 5555
E2E構築の目的
E2E基盤はfreeeにおいて以前から存在し、
QAチームをしばらく1人で切り盛りしていたメンバーが日々のQAテストの中で作り上げていました。
今までのQAチームの流れを組み、人力で解決すべきところは新規機能の箇所に絞ることで効果的なQA体制を築けることを目指しています。
社会に届けているサービスの品質保全をしっかりやるためでもあります。
E2E基盤構築を担当してみて思ったこと
会計ソフトウェアは複雑
会計freee のE2Eテストを書いているのですが、機能の豊富さを毎日実感しています。
テストシナリオに沿ったテストを書くようにしていますが、16
請求書を作成した後取引が作成、その後取引で使われた勘定科目の金額を修正した後に請求書側でどのように見えるのか?
といったテストケースを書いています。これだけでも複雑なテストケース対応が必要です。17
E2Eテストはもっと泥臭いものだと思っていた
Capybara + SitePrism を使うことで、アプリケーションコードを書いているような感覚でコードのメンテナンス・開発を行えています。
小さなスクリプトを大量に作っていくもの == 事前条件、グローバル変数利用、再利用は難しい ものになるのかと予想していたのですが、予想が覆りました。
テストケースを書いていてかなり楽しいです。
テストを検証する時間が足りなくなる
1つの操作について、機械的に手早く入力し別の画面に遷移するとAPI通信が間に合わず、ページ遷移後にエラーになることがあります。
そのため適切にsleepや、DOMレンダリング待ちの操作を挟むのですが、その度にテストの速度が遅くなり、テストケースの動作検証に時間がかかるようになります。
localhostでテストケースを動かしてみた後、staging環境でテストケースを動かすと速度差があるためstaging環境だとDOMレンダリングが行われテストが通るケースがありました。
総括
- Capybara + SitePrism の組み合わせはかなり良い(2016年12月現在)
- Selenium hub + node の基盤を構築することでテスト実行を並列化し、スケールアウトすることが可能
- エンジニアがこういった基盤構築、テストケース構築にアサインされるのは良いのかなと感じている
次のアドベントカレンダー担当は
freee株式会社 フロントエンドエンジニア @tohashi が 2016/12/18(日) の担当です。
参考プレゼン資料: フロントエンドのモダン化とJavaScriptモジュールの
依存解決
ご期待ください(`・ω・´)ゞ
注釈
テストの実行にはchromedriverが必要です。homebrewであればbrew install chromedriver でインストールできます。また、成果物をご利用の場合はbundle installを行ってください。
-
他のプロダクト開発と兼務しています。 ↩
-
Capybara はChromedriverなどを使ってブラウザを操作するソリューションを提供します。SitePrism はCapybaraに対しPageObjectsパターンを実装できるソリューションを提供します。 ↩
-
Gemfileやspec_helperについては省いています。詳しくは成果物をご参考ください。 ↩
- ↩
-
https://developers.google.com/web/tools/chrome-devtools/console/command-line-reference#xpath ↩
-
jQueryベースのAjax通信を待ちたい場合、Qiita記事 CapybaraのJSテストがrandom failするに詳しく記載されています。 ↩
-
SitePrismと合わせてインストールされるため、改めてgem installを行う必要はありません。 ↩
-
現在進行系です。Selenium ユーザーの方々に知見を求めにいくかもしれません。 ↩
-
単一マシン上で動作するstandalone モードもあります ↩
-
ヘッドレスブラウザのphantomjsもselenium nodeになれます。docker imageが作成できる成果物もあります。 ↩
-
Ubuntuをご利用されている場合は別途バイナリをダウンロードし、実行できるようにPATHを通す必要があります ↩
-
VNC環境が整ったdocker imageを利用するには理由があり、IPAフォントがインストールされるようにDockerfileが調整されているため、日本語が文字化けしないためです。 ↩
-
desired_capabilities の指定により chrome が動いている node のみテストが実行されるようになります。 ↩
-
テストケースは一通りGoogleスプレッドシートに書き、未実装・実装を切り分けて進めています。PICTを利用し、テストケースの洗い出し、必要なものの選定をする必要があるのかなと感じています。 ↩
-
対象の償却対象の減価償却率が法令改定前なのか、請求書であれば源泉徴収税は自動計算された状態で表示されているのか、取引先は初回編集時と更新時で変えた結果、pdf出力の内容が変更されているか、など。複雑です。 ↩