E2Eテスト基盤開発を担当して

  • 97
    いいね
  • 0
    コメント

freee Engineers Advent Calendar 2016
12月17日担当の @futoase です。

現在、E2Eテスト基盤構築の担当をしています。1
CapybaraSitePrism および Selenium に触れていく中で自分や弊社メンバーから得た知見について記載します。

4点の内容となります。

  • Capybara + SitePrism
  • Selenium
  • E2Eの目的
  • E2E基盤構築を担当してみて思ったこと

Capybara + SitePrism

同僚の @kompiroCapybara + SitePrism を使うことを提案、フレームワーク化を行ってくれたのでテストケース作成に利用しています。2

Selenium/Appium Advent Calendar 2016にてSitePrismを利用したPageObjectsパターンを使ったテスト作成について網羅的に説明した記事を書いています。

RubyでPageObjectsパターンを実装できる SitePrism のご紹介

例として弊社のランディングページに対して
Capybara + SitePrism によるPageObjects, specを書いています。
PageObjectsとテスト(spec)になります。3

PageObjects

  • ログイン画面(secure.freee.co.jp)
pages/secure/index.rb
require 'site_prism'

module Secure
  class Login < SitePrism::Page
    set_url 'https://secure.freee.co.jp/users/login/'
  end
end
pages/www/index.rb
require 'site_prism'

module WWW
  # LP
  class Index < SitePrism::Page
    set_url '/'

    element :login_link, 'div.ncms-mod-header1__login a'
  end
end
  • テスト
index_spec.rb
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

lp.png

要素の指定

Capybaraではデフォルトではcss selectorにより指定するようになっています。取得したい要素を調べるにははchrome devtoolsを使います。

anchor.png

Screen Shot 2016-12-16 at 0.23.24.png

ログインボタン要素の取得が確認できたら、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要素を取得することができます。

Screen Shot 2016-12-16 at 0.40.58.png

マウスクリック、キーアサイン

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にあげました。
ぜひ参考にしてください。

https://github.com/futoase/capybara-site_prism-sample

Selenium

E2Eは性格上、テストの実行に時間がかかります。
テストを実行するプロセスを増やすアプローチを取り、並列化することで解消しようと試みています。9
Seleniumを利用してhub, nodeの基盤を構築することでスケールアウトする仕組みを作ることが可能です。

selenium hub, node

seleniumはhub, nodeモードがあります。10

selenium.png

  • 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

docker-compose.yml
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/ にアクセスできます。

Screen Shot 2016-12-16 at 20.45.44.png

selenium nodeへの接続はFinderを立ち上げ、⌘+KでVNCサーバに接続することができます。
(docker-composeの設定で、localhost:15900に対して接続します)

Screen Shot 2016-12-16 at 20.59.28.png

passwordは"secret"と入力し、接続します

Screen Shot 2016-12-16 at 21.05.51.png

Screen Shot 2016-12-16 at 21.15.39.png

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

vnc.png

nodeを別ホストで運用するには

selenium nodeを別ホストで運用することも可能です。

ただし、docker-seleniumを利用してnodeを立ち上げるには注意点があります。 

node自身がサーブレットにアクセスする必要があるため、5555ポートに対し自分自身(node)を指定する必要があります。そのためのオプション引数を適用するには、Issueに上がっているパッチの適用が必要(2016/12/17現在)になっています。

Selenium HQ wikiにかかれています

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アドレス、ポート番号を渡して立ち上げる形になります。

docker-compose.yml
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モジュールの
依存解決

ご期待ください(`・ω・´)ゞ

注釈


  1. 他のプロダクト開発と兼務しています。 

  2. Capybara はChromedriverなどを使ってブラウザを操作するソリューションを提供します。SitePrism はCapybaraに対しPageObjectsパターンを実装できるソリューションを提供します。 

  3. Gemfileやspec_helperについては省いています。詳しくは成果物をご参考ください。 

  4. テストの実行にはchromedriverが必要です。homebrewであればbrew install chromedriver でインストールできます。また、成果物をご利用の場合はbundle installを行ってください。 

  5. https://developers.google.com/web/tools/chrome-devtools/console/command-line-reference#xpath 

  6. Method: Capybara::Node::Element#send_keys 

  7. jQueryベースのAjax通信を待ちたい場合、Qiita記事 CapybaraのJSテストがrandom failするに詳しく記載されています。 

  8. SitePrismと合わせてインストールされるため、改めてgem installを行う必要はありません。 

  9. 現在進行系です。Selenium ユーザーの方々に知見を求めにいくかもしれません。 

  10. 単一マシン上で動作するstandalone モードもあります 

  11. debugとsuffixが付いているイメージはVNC環境がプリインストールされているものです。 

  12. ヘッドレスブラウザのphantomjsもselenium nodeになれます。docker imageが作成できる成果物もあります。 

  13. Ubuntuをご利用されている場合は別途バイナリをダウンロードし、実行できるようにPATHを通す必要があります 

  14. VNC環境が整ったdocker imageを利用するには理由があり、IPAフォントがインストールされるようにDockerfileが調整されているため、日本語が文字化けしないためです。 

  15. desired_capabilities の指定により chrome が動いている node のみテストが実行されるようになります。 

  16. テストケースは一通りGoogleスプレッドシートに書き、未実装・実装を切り分けて進めています。PICTを利用し、テストケースの洗い出し、必要なものの選定をする必要があるのかなと感じています。 

  17. 対象の償却対象の減価償却率が法令改定前なのか、請求書であれば源泉徴収税は自動計算された状態で表示されているのか、取引先は初回編集時と更新時で変えた結果、pdf出力の内容が変更されているか、など。複雑です。