Upgrade to Pro — share decks privately, control downloads, hide ads and more …

なぜE2Eテストがたまに落ちるのか / How do E2E tests fail randomly

なぜE2Eテストがたまに落ちるのか / How do E2E tests fail randomly

Fb1b9f3d7332a7a7e262b70013b5f7dd?s=128

Fumiaki MATSUSHIMA

July 14, 2018
Tweet

Transcript

  1. なぜ E2E テストがたまに落ちるのか @mtsmfm Fumiaki Matsushima Rails Developers Meetup 2018

    Day 3 Extreme
  2. ➔ Quipper Ltd ➔ Rubyと麻雀とDbDが好き ➔ 西日暮里.rb主催 ➔ GraphQL Tokyo

    主催 @mtsmfm.inspect
  3. https://studysapuri.jp/

  4. https://techplay.jp/event/680406

  5. None
  6. https://www.careertrek.com/jobs/view/614827/com_detail

  7. なぜ E2E テストがたまに落ちるのか @mtsmfm Fumiaki Matsushima Rails Developers Meetup 2018

    Day 3 Extreme
  8. ➔ ユーザの操作を再現して行うテスト ◆ ここでは実ブラウザ、特に Chrome を用いるものにつ いて ➔ Rails System

    Test ➔ Feature Spec / System Spec ➔ Cucumber / Turnip E2E テスト
  9. https://github.com/rails/rails/releases/v5.1.0 もはや1年以上前

  10. E2E テスト 書いてますか

  11. ➔ E2E テストを書いている ◆ プロジェクトで既に導入されていた ◆ どうやって動いているかはあまり知らない ➔ たまに落ちて困っている 想定読者

  12. Agenda | 01 Capybara の visit の向こう側 02 落ちる原因と対策 03

    たまに落ちるテストと向き合う
  13. 01 Capybara の visit の向こう側

  14. visit "/"

  15. Minitest Capybara Rails Chrome visit “/” localhost:XXXX で起動 起動 localhost:XXXX

    へ GET localhost:XXXX
  16. Minitest Capybara Rails Chrome visit “/” localhost:XXXX で起動 起動 localhost:XXXX

    へ GET localhost:XXXX
  17. https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/dsl.rb#L49-L54

  18. https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara.rb#L299

  19. https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/session.rb#L89

  20. https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/server.rb#L68-L70

  21. https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara.rb#L460-L464

  22. ➔ visit 時に Rails の実サーバが test モードで起動する ◆ Initializers の類の

    stub タイミングには注意 ➔ 同一プロセスで動いている ◆ そのおかげでテスト中の stub などが効く ポイント
  23. Minitest Capybara Rails Chrome visit “/” localhost:XXXX で起動 起動 localhost:XXXX

    へ GET localhost:XXXX
  24. Capybara selenium- webdriver 初期化 起動 Chrome Driver 起動 Chrome

  25. https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/session.rb#L269

  26. https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara.rb#L492-L494

  27. https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/selenium/driver.rb#L59

  28. https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/selenium/driver.rb#L31

  29. https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/chro me/driver.rb#L43

  30. https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/chro me/service.rb#L27

  31. https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/chro me/driver.rb#L48

  32. https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remo te/bridge.rb#L53

  33. https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remo te/bridge.rb#L97

  34. https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remo te/bridge.rb#L164

  35. https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remo te/bridge.rb#L27

  36. https://github.com/chromium/chromium/tree/69.0.3489.2/chrome/test/chromedriver

  37. https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/server/htt p_handler.cc#L91

  38. https://github.com/chromium/chromium/blob/69.0.3491.1/chrome/test/chromedriver/session_co mmands.cc#L341

  39. https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/session_co mmands.cc

  40. https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/chrome_la uncher.cc

  41. Minitest Capybara Rails Chrome visit “/” localhost:XXXX で起動 起動 localhost:XXXX

    へ GET localhost:XXXX
  42. https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/selenium/driver.rb#L59

  43. https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/com mon/navigation.rb#L30

  44. https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remo te/oss/bridge.rb#L50

  45. https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remo te/oss/commands.rb#L39

  46. ➔ Capybara は selenium-webdriver 越しの ChromeDriver 越しに Chrome を起動している ➔

    ChromeDriver とは REST API でやりとりしている ◆ WebDriver の仕様がある https://w3c.github.io/webdriver/ ➔ Capybara の DSL は driver に処理を移譲している ◆ driver を差し替えられるようにしている ポイント
  47. Agenda | 01 Capybara の visit の向こう側 02 落ちる原因と対策 03

    たまに落ちるテストと向き合う
  48. 02 落ちる原因と対策

  49. クリックに 失敗する

  50. ➔ 失敗時のスクショを見ると、遷移ができていない ➔ 90% くらいこれ クリックに失敗する

  51. ➔ クリック処理は2段階に分かれている ◆ 1. 要素の座標を求める ◆ 2. 座標をクリックする ➔ 1

    と 2 の間に要素の座標が変わると見当違いの場所を クリックすることになる クリックに失敗する (原因)
  52. https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/selenium/node.rb#L87

  53. https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/com mon/element.rb#L72

  54. https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remo te/oss/commands.rb#L118

  55. https://github.com/chromium/chromium/blob/18bace42984d03e65cc2ebb76b8e7cd0ca2c99db/ch rome/test/chromedriver/server/http_handler.cc#L199-L201

  56. https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/element_c ommands.cc

  57. https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/element_c ommands.cc

  58. https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/element_c ommands.cc

  59. http://chromedriver.chromium.org/help/clicking-issues

  60. https://w3c.github.io/webdriver/#element-click

  61. ➔ 要素の座標が変わりやすいもの ◆ アニメーション • 想像がつきやすいので自前で待つ処理が入って いることが多い ◆ 画像読み込み完了時にガクッとなるような CSS

    に なっている • 見落としがち クリックに失敗する (原因)
  62. ➔ 要素の座標が変わりやすいもの ◆ アニメーション • 想像がつきやすいので自前で待つ処理が入って いることが多い ◆ 画像読み込み完了時にガクッとなるような CSS

    に なっている • 見落としがち クリックに失敗する (原因)
  63. https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/element_c ommands.cc

  64. https://github.com/teamcapybara/capybara/pull/2042/files

  65. ➔ 要素の座標が変わりやすいもの ◆ アニメーション • 想像がつきやすいので自前で待つ処理が入って いることが多い ◆ 画像読み込み完了時にガクッとなるような CSS

    に なっている • 見落としがち クリックに失敗する (原因)
  66. つまりユーザも クリックミスるかも

  67. とりあえずの回避

  68. def wait_for_image_loading Timeout.timeout(Capybara.default_wait_time) do sleep 0.5 until evaluate_script(<<~JS) Array.prototype.every.call( document.querySelectorAll("img"),

    (e) => e.complete ) JS end end
  69. ➔ ユーザが読み込みが終わったと判断するのと同じ方法で 待つ ➔ テスト中は !important とかでアニメーション無効にしてみ る ➔ ユーザが触る時にも要素の位置がずれ得るスタイルに

    なっていないか確認する ◆ 画像の読み込みを待ってみる クリックに失敗する (対策)
  70. Assertion 後に サーバーエラー

  71. ➔ テスト本体の assertion は通っているが、404 で落ちてい る ◆ DB がまっさらだと起きそうなエラー内容 Assertion

    後にサーバーエラー
  72. ➔ DBクリーンアップ処理が終わったあとにブラウザから ポーリングなどによりリクエストが飛びサーバーエラー (404 とか) Assertion 後にサーバーエラー (原因)

  73. BEGIN TRANSACTION Minitest DB Rails Chrome SELECT GET /users/1 ROLLBACK

    create(:user) visit ブラウザ終了
  74. Chrome GET /users/1 (ポーリング) BEGIN TRANSACTION Minitest DB Rails SELECT

    GET /users/1 ROLLBACK create(:user) visit SELECT ブラウザ終了
  75. Chrome GET /users/1 (ポーリング) BEGIN TRANSACTION Minitest DB Rails SELECT

    GET /users/1 ROLLBACK create(:user) visit SELECT ブラウザ終了
  76. https://github.com/mtsmfm/rails/commit/1a197e4

  77. GET /users/1 (ポーリング) BEGIN TRANSACTION Minitest DB Rails SELECT GET

    /users/1 ROLLBACK create(:user) visit SELECT ブラウザ終了 Chrome
  78. https://github.com/rails/rails/pull/28223/files

  79. ➔ ブラウザの終了処理をDBクリーンアップより先にやらな いといけない ◆ Rails System Test はリリース前に直した ➔ 自分で

    database-cleaner など teardown 処理を入れて いる場合には、順番に注意 Assertion 後にサーバーエラー (対策)
  80. 何もしてないのに ログイン済み

  81. ➔ ログイン処理前なのにログインが済んでいることになって いる ➔ 直前のテストのセッションが残っている 何もしてないのにログイン済み

  82. ➔ ブラウザのクリーンアップは Cookie 削除してから blank ページを開く ➔ Cookie 削除前に開始したリクエストが、Cookie 削除後

    に完了すると Cookie が消されない 何もしてないのにログイン済み (原因)
  83. Minitest Chrome Cookie 削除 teardown Capybar a about:blank へ

  84. Set-Cookie Minitest Chrome Rails GET Cookie 削除 teardown Capybar a

    about:blank へ
  85. https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/selenium/driver.rb#L127

  86. ➔ blank ページにいる状態で全 cookie を消す 何もしてないのにログイン済み (対策)

  87. そんな API はない

  88. ➔ 現在開いているドメイン以外の Cookie を消す方法がな い... ◆ モンキーパッチでお茶を濁しているが... 何もしてないのにログイン済み (対策) Capybara::Selenium::Driver.prepend(Module.new

    do def reset! quit end end)
  89. Agenda | 01 Capybara の visit の向こう側 02 落ちる原因と対策 03

    たまに落ちるテストと向き合う
  90. 03 たまに落ちるテストと向き合う

  91. ➔ 直すのめちゃくちゃめんどくさい ◆ デバッグが大変 ◆ 再現しない ➔ テストの書き方の問題やライブラリの問題なだけで、本番 影響がないことが多い ◆

    いっそ無視 直しても費用対効果がよくない
  92. ➔ rspec-retry ◆ Retry 時のクリーンアップ処理まで考慮しきれず、 ちゃんと Retry できない場合も ➔ 既出かどうかのデータを集めて無視する?

    無視する方法
  93. https://ai.google/research/pubs/pub46593

  94. ➔ 420万あるテストの結果を集積しまくっている ➔ 16%はたまに落ちるテスト ➔ テスト結果が変化するとき、84%は不安定なテストによる もの Advances in Continuous

    Integration Testing at Google
  95. ➔ 過去に落ちたテスト一覧とか出してくれる ➔ ちょっと情報が足りない Circle CI

  96. ➔ テスト結果集積基盤を自前で作る? ◆ Quipper にはまだない ◆ 取り組みを教えて欲しい ➔ OSS 化の余地がありそう

    Future work
  97. ➔ Capybara とか DB とか ChromeDriver とかがどう絡み 合ってるか知ってると、何かおかしいときにアタリがつくの で覚えて損はない ◆

    別言語でも似たような構成にはなるはず ➔ だいたいの場合クリックに失敗してる ◆ わかっててもミスる ➔ たまに落ちるやつをうまいこと無視する仕組みが欲しい まとめ