Rails Developers Meetup 2018 Day 3 Extreme 発表資料
https://techplay.jp/event/679666
YouTube: https://www.youtube.com/watch?v=1U6SDJD_sp8&t=0s&list=PLLpMWAO4_8wIvjyxzS6p1XpvpOM9v_u3z&index=48
なぜ E2E テストがたまに落ちるのか@mtsmfm Fumiaki MatsushimaRails Developers Meetup 2018 Day 3 Extreme
View Slide
➔ Quipper Ltd➔ Rubyと麻雀とDbDが好き➔ 西日暮里.rb主催➔ GraphQL Tokyo 主催@mtsmfm.inspect
https://studysapuri.jp/
https://techplay.jp/event/680406
https://www.careertrek.com/jobs/view/614827/com_detail
➔ ユーザの操作を再現して行うテスト◆ ここでは実ブラウザ、特に Chrome を用いるものについて➔ Rails System Test➔ Feature Spec / System Spec➔ Cucumber / TurnipE2E テスト
https://github.com/rails/rails/releases/v5.1.0もはや1年以上前
E2E テスト書いてますか
➔ E2E テストを書いている◆ プロジェクトで既に導入されていた◆ どうやって動いているかはあまり知らない➔ たまに落ちて困っている想定読者
Agenda | 01 Capybara の visit の向こう側02 落ちる原因と対策03 たまに落ちるテストと向き合う
01 Capybara の visit の向こう側
visit "/"
Minitest Capybara Rails Chromevisit “/”localhost:XXXXで起動起動localhost:XXXX へGET localhost:XXXX
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/dsl.rb#L49-L54
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara.rb#L299
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/session.rb#L89
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/server.rb#L68-L70
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara.rb#L460-L464
➔ visit 時に Rails の実サーバが test モードで起動する◆ Initializers の類の stub タイミングには注意➔ 同一プロセスで動いている◆ そのおかげでテスト中の stub などが効くポイント
Capybaraselenium-webdriver初期化起動ChromeDriver起動Chrome
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/session.rb#L269
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara.rb#L492-L494
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/selenium/driver.rb#L59
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/selenium/driver.rb#L31
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/chrome/driver.rb#L43
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/chrome/service.rb#L27
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/chrome/driver.rb#L48
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remote/bridge.rb#L53
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remote/bridge.rb#L97
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remote/bridge.rb#L164
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remote/bridge.rb#L27
https://github.com/chromium/chromium/tree/69.0.3489.2/chrome/test/chromedriver
https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/server/http_handler.cc#L91
https://github.com/chromium/chromium/blob/69.0.3491.1/chrome/test/chromedriver/session_commands.cc#L341
https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/session_commands.cc
https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/chrome_launcher.cc
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/common/navigation.rb#L30
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remote/oss/bridge.rb#L50
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remote/oss/commands.rb#L39
➔ Capybara は selenium-webdriver 越しのChromeDriver 越しに Chrome を起動している➔ ChromeDriver とは REST API でやりとりしている◆ WebDriver の仕様があるhttps://w3c.github.io/webdriver/➔ Capybara の DSL は driver に処理を移譲している◆ driver を差し替えられるようにしているポイント
02 落ちる原因と対策
クリックに失敗する
➔ 失敗時のスクショを見ると、遷移ができていない➔ 90% くらいこれクリックに失敗する
➔ クリック処理は2段階に分かれている◆ 1. 要素の座標を求める◆ 2. 座標をクリックする➔ 1 と 2 の間に要素の座標が変わると見当違いの場所をクリックすることになるクリックに失敗する (原因)
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/selenium/node.rb#L87
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/common/element.rb#L72
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remote/oss/commands.rb#L118
https://github.com/chromium/chromium/blob/18bace42984d03e65cc2ebb76b8e7cd0ca2c99db/chrome/test/chromedriver/server/http_handler.cc#L199-L201
https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/element_commands.cc
http://chromedriver.chromium.org/help/clicking-issues
https://w3c.github.io/webdriver/#element-click
➔ 要素の座標が変わりやすいもの◆ アニメーション● 想像がつきやすいので自前で待つ処理が入っていることが多い◆ 画像読み込み完了時にガクッとなるような CSS になっている● 見落としがちクリックに失敗する (原因)
https://github.com/teamcapybara/capybara/pull/2042/files
つまりユーザもクリックミスるかも
とりあえずの回避
def wait_for_image_loadingTimeout.timeout(Capybara.default_wait_time) dosleep 0.5 until evaluate_script(<<~JS)Array.prototype.every.call(document.querySelectorAll("img"),(e) => e.complete)JSendend
➔ ユーザが読み込みが終わったと判断するのと同じ方法で待つ➔ テスト中は !important とかでアニメーション無効にしてみる➔ ユーザが触る時にも要素の位置がずれ得るスタイルになっていないか確認する◆ 画像の読み込みを待ってみるクリックに失敗する (対策)
Assertion 後にサーバーエラー
➔ テスト本体の assertion は通っているが、404 で落ちている◆ DB がまっさらだと起きそうなエラー内容Assertion 後にサーバーエラー
➔ DBクリーンアップ処理が終わったあとにブラウザからポーリングなどによりリクエストが飛びサーバーエラー(404 とか)Assertion 後にサーバーエラー (原因)
BEGINTRANSACTIONMinitest DB Rails ChromeSELECTGET /users/1ROLLBACKcreate(:user)visitブラウザ終了
ChromeGET /users/1(ポーリング)BEGINTRANSACTIONMinitest DB RailsSELECTGET /users/1ROLLBACKcreate(:user)visitSELECTブラウザ終了
https://github.com/mtsmfm/rails/commit/1a197e4
GET /users/1(ポーリング)BEGINTRANSACTIONMinitest DB RailsSELECTGET /users/1ROLLBACKcreate(:user)visitSELECTブラウザ終了Chrome
https://github.com/rails/rails/pull/28223/files
➔ ブラウザの終了処理をDBクリーンアップより先にやらないといけない◆ Rails System Test はリリース前に直した➔ 自分で database-cleaner など teardown 処理を入れている場合には、順番に注意Assertion 後にサーバーエラー (対策)
何もしてないのにログイン済み
➔ ログイン処理前なのにログインが済んでいることになっている➔ 直前のテストのセッションが残っている何もしてないのにログイン済み
➔ ブラウザのクリーンアップは Cookie 削除してから blankページを開く➔ Cookie 削除前に開始したリクエストが、Cookie 削除後に完了すると Cookie が消されない何もしてないのにログイン済み (原因)
Minitest ChromeCookie 削除teardownCapybaraabout:blank へ
Set-CookieMinitest Chrome RailsGETCookie 削除teardownCapybaraabout:blank へ
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/selenium/driver.rb#L127
➔ blank ページにいる状態で全 cookie を消す何もしてないのにログイン済み (対策)
そんな API はない
➔ 現在開いているドメイン以外の Cookie を消す方法がない...◆ モンキーパッチでお茶を濁しているが...何もしてないのにログイン済み (対策)Capybara::Selenium::Driver.prepend(Module.new dodef reset!quitendend)
03 たまに落ちるテストと向き合う
➔ 直すのめちゃくちゃめんどくさい◆ デバッグが大変◆ 再現しない➔ テストの書き方の問題やライブラリの問題なだけで、本番影響がないことが多い◆ いっそ無視直しても費用対効果がよくない
➔ rspec-retry◆ Retry 時のクリーンアップ処理まで考慮しきれず、ちゃんと Retry できない場合も➔ 既出かどうかのデータを集めて無視する?無視する方法
https://ai.google/research/pubs/pub46593
➔ 420万あるテストの結果を集積しまくっている➔ 16%はたまに落ちるテスト➔ テスト結果が変化するとき、84%は不安定なテストによるものAdvances in Continuous Integration Testing at Google
➔ 過去に落ちたテスト一覧とか出してくれる➔ ちょっと情報が足りないCircle CI
➔ テスト結果集積基盤を自前で作る?◆ Quipper にはまだない◆ 取り組みを教えて欲しい➔ OSS 化の余地がありそうFuture work
➔ Capybara とか DB とか ChromeDriver とかがどう絡み合ってるか知ってると、何かおかしいときにアタリがつくので覚えて損はない◆ 別言語でも似たような構成にはなるはず➔ だいたいの場合クリックに失敗してる◆ わかっててもミスる➔ たまに落ちるやつをうまいこと無視する仕組みが欲しいまとめ