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

Fumiaki MATSUSHIMA

July 14, 2018
Tweet

More Decks by Fumiaki MATSUSHIMA

Other Decks in Programming

Transcript

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

    View Slide

  2. ➔ Quipper Ltd
    ➔ Rubyと麻雀とDbDが好き
    ➔ 西日暮里.rb主催
    ➔ GraphQL Tokyo 主催
    @mtsmfm.inspect

    View Slide

  3. https://studysapuri.jp/

    View Slide

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

    View Slide

  5. View Slide

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

    View Slide

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

    View Slide

  8. ➔ ユーザの操作を再現して行うテスト
    ◆ ここでは実ブラウザ、特に Chrome を用いるものにつ
    いて
    ➔ Rails System Test
    ➔ Feature Spec / System Spec
    ➔ Cucumber / Turnip
    E2E テスト

    View Slide

  9. https://github.com/rails/rails/releases/v5.1.0
    もはや1年以上前

    View Slide

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

    View Slide

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

    View Slide

  12. Agenda | 01 Capybara の visit の向こう側
    02 落ちる原因と対策
    03 たまに落ちるテストと向き合う

    View Slide

  13. 01 Capybara の visit の向こう側

    View Slide

  14. visit "/"

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  22. ➔ visit 時に Rails の実サーバが test モードで起動する
    ◆ Initializers の類の stub タイミングには注意
    ➔ 同一プロセスで動いている
    ◆ そのおかげでテスト中の stub などが効く
    ポイント

    View Slide

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

    View Slide

  24. Capybara
    selenium-
    webdriver
    初期化
    起動
    Chrome
    Driver
    起動
    Chrome

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  46. ➔ Capybara は selenium-webdriver 越しの
    ChromeDriver 越しに Chrome を起動している
    ➔ ChromeDriver とは REST API でやりとりしている
    ◆ WebDriver の仕様がある
    https://w3c.github.io/webdriver/
    ➔ Capybara の DSL は driver に処理を移譲している
    ◆ driver を差し替えられるようにしている
    ポイント

    View Slide

  47. Agenda | 01 Capybara の visit の向こう側
    02 落ちる原因と対策
    03 たまに落ちるテストと向き合う

    View Slide

  48. 02 落ちる原因と対策

    View Slide

  49. クリックに
    失敗する

    View Slide

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

    View Slide

  51. ➔ クリック処理は2段階に分かれている
    ◆ 1. 要素の座標を求める
    ◆ 2. 座標をクリックする
    ➔ 1 と 2 の間に要素の座標が変わると見当違いの場所を
    クリックすることになる
    クリックに失敗する (原因)

    View Slide

  52. https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/selenium/node.rb#L87

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  66. つまりユーザも
    クリックミスるかも

    View Slide

  67. とりあえずの回避

    View Slide

  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

    View Slide

  69. ➔ ユーザが読み込みが終わったと判断するのと同じ方法で
    待つ
    ➔ テスト中は !important とかでアニメーション無効にしてみ

    ➔ ユーザが触る時にも要素の位置がずれ得るスタイルに
    なっていないか確認する
    ◆ 画像の読み込みを待ってみる
    クリックに失敗する (対策)

    View Slide

  70. Assertion 後に
    サーバーエラー

    View Slide

  71. ➔ テスト本体の assertion は通っているが、404 で落ちてい

    ◆ DB がまっさらだと起きそうなエラー内容
    Assertion 後にサーバーエラー

    View Slide

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

    View Slide

  73. BEGIN
    TRANSACTION
    Minitest DB Rails Chrome
    SELECT
    GET /users/1
    ROLLBACK
    create(:user)
    visit
    ブラウザ終了

    View Slide

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

    View Slide

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

    View Slide

  76. https://github.com/mtsmfm/rails/commit/1a197e4

    View Slide

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

    View Slide

  78. https://github.com/rails/rails/pull/28223/files

    View Slide

  79. ➔ ブラウザの終了処理をDBクリーンアップより先にやらな
    いといけない
    ◆ Rails System Test はリリース前に直した
    ➔ 自分で database-cleaner など teardown 処理を入れて
    いる場合には、順番に注意
    Assertion 後にサーバーエラー (対策)

    View Slide

  80. 何もしてないのに
    ログイン済み

    View Slide

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

    View Slide

  82. ➔ ブラウザのクリーンアップは Cookie 削除してから blank
    ページを開く
    ➔ Cookie 削除前に開始したリクエストが、Cookie 削除後
    に完了すると Cookie が消されない
    何もしてないのにログイン済み (原因)

    View Slide

  83. Minitest Chrome
    Cookie 削除
    teardown
    Capybar
    a
    about:blank へ

    View Slide

  84. Set-Cookie
    Minitest Chrome Rails
    GET
    Cookie 削除
    teardown
    Capybar
    a
    about:blank へ

    View Slide

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

    View Slide

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

    View Slide

  87. そんな API はない

    View Slide

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

    View Slide

  89. Agenda | 01 Capybara の visit の向こう側
    02 落ちる原因と対策
    03 たまに落ちるテストと向き合う

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  93. https://ai.google/research/pubs/pub46593

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  97. ➔ Capybara とか DB とか ChromeDriver とかがどう絡み
    合ってるか知ってると、何かおかしいときにアタリがつくの
    で覚えて損はない
    ◆ 別言語でも似たような構成にはなるはず
    ➔ だいたいの場合クリックに失敗してる
    ◆ わかっててもミスる
    ➔ たまに落ちるやつをうまいこと無視する仕組みが欲しい
    まとめ

    View Slide