なぜE2Eテストがたまに落ちるのか / How do E2E tests fail randomly
by
Fumiaki MATSUSHIMA
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
なぜ E2E テストがたまに落ちるのか @mtsmfm Fumiaki Matsushima Rails Developers Meetup 2018 Day 3 Extreme
Slide 2
Slide 2 text
➔ Quipper Ltd ➔ Rubyと麻雀とDbDが好き ➔ 西日暮里.rb主催 ➔ GraphQL Tokyo 主催 @mtsmfm.inspect
Slide 3
Slide 3 text
https://studysapuri.jp/
Slide 4
Slide 4 text
https://techplay.jp/event/680406
Slide 5
Slide 5 text
No content
Slide 6
Slide 6 text
https://www.careertrek.com/jobs/view/614827/com_detail
Slide 7
Slide 7 text
なぜ E2E テストがたまに落ちるのか @mtsmfm Fumiaki Matsushima Rails Developers Meetup 2018 Day 3 Extreme
Slide 8
Slide 8 text
➔ ユーザの操作を再現して行うテスト ◆ ここでは実ブラウザ、特に Chrome を用いるものにつ いて ➔ Rails System Test ➔ Feature Spec / System Spec ➔ Cucumber / Turnip E2E テスト
Slide 9
Slide 9 text
https://github.com/rails/rails/releases/v5.1.0 もはや1年以上前
Slide 10
Slide 10 text
E2E テスト 書いてますか
Slide 11
Slide 11 text
➔ E2E テストを書いている ◆ プロジェクトで既に導入されていた ◆ どうやって動いているかはあまり知らない ➔ たまに落ちて困っている 想定読者
Slide 12
Slide 12 text
Agenda | 01 Capybara の visit の向こう側 02 落ちる原因と対策 03 たまに落ちるテストと向き合う
Slide 13
Slide 13 text
01 Capybara の visit の向こう側
Slide 14
Slide 14 text
visit "/"
Slide 15
Slide 15 text
Minitest Capybara Rails Chrome visit “/” localhost:XXXX で起動 起動 localhost:XXXX へ GET localhost:XXXX
Slide 16
Slide 16 text
Minitest Capybara Rails Chrome visit “/” localhost:XXXX で起動 起動 localhost:XXXX へ GET localhost:XXXX
Slide 17
Slide 17 text
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/dsl.rb#L49-L54
Slide 18
Slide 18 text
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara.rb#L299
Slide 19
Slide 19 text
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/session.rb#L89
Slide 20
Slide 20 text
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/server.rb#L68-L70
Slide 21
Slide 21 text
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara.rb#L460-L464
Slide 22
Slide 22 text
➔ visit 時に Rails の実サーバが test モードで起動する ◆ Initializers の類の stub タイミングには注意 ➔ 同一プロセスで動いている ◆ そのおかげでテスト中の stub などが効く ポイント
Slide 23
Slide 23 text
Minitest Capybara Rails Chrome visit “/” localhost:XXXX で起動 起動 localhost:XXXX へ GET localhost:XXXX
Slide 24
Slide 24 text
Capybara selenium- webdriver 初期化 起動 Chrome Driver 起動 Chrome
Slide 25
Slide 25 text
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/session.rb#L269
Slide 26
Slide 26 text
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara.rb#L492-L494
Slide 27
Slide 27 text
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/selenium/driver.rb#L59
Slide 28
Slide 28 text
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/selenium/driver.rb#L31
Slide 29
Slide 29 text
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/chro me/driver.rb#L43
Slide 30
Slide 30 text
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/chro me/service.rb#L27
Slide 31
Slide 31 text
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/chro me/driver.rb#L48
Slide 32
Slide 32 text
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remo te/bridge.rb#L53
Slide 33
Slide 33 text
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remo te/bridge.rb#L97
Slide 34
Slide 34 text
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remo te/bridge.rb#L164
Slide 35
Slide 35 text
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remo te/bridge.rb#L27
Slide 36
Slide 36 text
https://github.com/chromium/chromium/tree/69.0.3489.2/chrome/test/chromedriver
Slide 37
Slide 37 text
https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/server/htt p_handler.cc#L91
Slide 38
Slide 38 text
https://github.com/chromium/chromium/blob/69.0.3491.1/chrome/test/chromedriver/session_co mmands.cc#L341
Slide 39
Slide 39 text
https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/session_co mmands.cc
Slide 40
Slide 40 text
https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/chrome_la uncher.cc
Slide 41
Slide 41 text
Minitest Capybara Rails Chrome visit “/” localhost:XXXX で起動 起動 localhost:XXXX へ GET localhost:XXXX
Slide 42
Slide 42 text
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/selenium/driver.rb#L59
Slide 43
Slide 43 text
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/com mon/navigation.rb#L30
Slide 44
Slide 44 text
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remo te/oss/bridge.rb#L50
Slide 45
Slide 45 text
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remo te/oss/commands.rb#L39
Slide 46
Slide 46 text
➔ Capybara は selenium-webdriver 越しの ChromeDriver 越しに Chrome を起動している ➔ ChromeDriver とは REST API でやりとりしている ◆ WebDriver の仕様がある https://w3c.github.io/webdriver/ ➔ Capybara の DSL は driver に処理を移譲している ◆ driver を差し替えられるようにしている ポイント
Slide 47
Slide 47 text
Agenda | 01 Capybara の visit の向こう側 02 落ちる原因と対策 03 たまに落ちるテストと向き合う
Slide 48
Slide 48 text
02 落ちる原因と対策
Slide 49
Slide 49 text
クリックに 失敗する
Slide 50
Slide 50 text
➔ 失敗時のスクショを見ると、遷移ができていない ➔ 90% くらいこれ クリックに失敗する
Slide 51
Slide 51 text
➔ クリック処理は2段階に分かれている ◆ 1. 要素の座標を求める ◆ 2. 座標をクリックする ➔ 1 と 2 の間に要素の座標が変わると見当違いの場所を クリックすることになる クリックに失敗する (原因)
Slide 52
Slide 52 text
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/selenium/node.rb#L87
Slide 53
Slide 53 text
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/com mon/element.rb#L72
Slide 54
Slide 54 text
https://github.com/SeleniumHQ/selenium/blob/selenium-3.13.0/rb/lib/selenium/webdriver/remo te/oss/commands.rb#L118
Slide 55
Slide 55 text
https://github.com/chromium/chromium/blob/18bace42984d03e65cc2ebb76b8e7cd0ca2c99db/ch rome/test/chromedriver/server/http_handler.cc#L199-L201
Slide 56
Slide 56 text
https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/element_c ommands.cc
Slide 57
Slide 57 text
https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/element_c ommands.cc
Slide 58
Slide 58 text
https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/element_c ommands.cc
Slide 59
Slide 59 text
http://chromedriver.chromium.org/help/clicking-issues
Slide 60
Slide 60 text
https://w3c.github.io/webdriver/#element-click
Slide 61
Slide 61 text
➔ 要素の座標が変わりやすいもの ◆ アニメーション ● 想像がつきやすいので自前で待つ処理が入って いることが多い ◆ 画像読み込み完了時にガクッとなるような CSS に なっている ● 見落としがち クリックに失敗する (原因)
Slide 62
Slide 62 text
➔ 要素の座標が変わりやすいもの ◆ アニメーション ● 想像がつきやすいので自前で待つ処理が入って いることが多い ◆ 画像読み込み完了時にガクッとなるような CSS に なっている ● 見落としがち クリックに失敗する (原因)
Slide 63
Slide 63 text
https://github.com/chromium/chromium/blob/69.0.3489.2/chrome/test/chromedriver/element_c ommands.cc
Slide 64
Slide 64 text
https://github.com/teamcapybara/capybara/pull/2042/files
Slide 65
Slide 65 text
➔ 要素の座標が変わりやすいもの ◆ アニメーション ● 想像がつきやすいので自前で待つ処理が入って いることが多い ◆ 画像読み込み完了時にガクッとなるような CSS に なっている ● 見落としがち クリックに失敗する (原因)
Slide 66
Slide 66 text
つまりユーザも クリックミスるかも
Slide 67
Slide 67 text
とりあえずの回避
Slide 68
Slide 68 text
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
Slide 69
Slide 69 text
➔ ユーザが読み込みが終わったと判断するのと同じ方法で 待つ ➔ テスト中は !important とかでアニメーション無効にしてみ る ➔ ユーザが触る時にも要素の位置がずれ得るスタイルに なっていないか確認する ◆ 画像の読み込みを待ってみる クリックに失敗する (対策)
Slide 70
Slide 70 text
Assertion 後に サーバーエラー
Slide 71
Slide 71 text
➔ テスト本体の assertion は通っているが、404 で落ちてい る ◆ DB がまっさらだと起きそうなエラー内容 Assertion 後にサーバーエラー
Slide 72
Slide 72 text
➔ DBクリーンアップ処理が終わったあとにブラウザから ポーリングなどによりリクエストが飛びサーバーエラー (404 とか) Assertion 後にサーバーエラー (原因)
Slide 73
Slide 73 text
BEGIN TRANSACTION Minitest DB Rails Chrome SELECT GET /users/1 ROLLBACK create(:user) visit ブラウザ終了
Slide 74
Slide 74 text
Chrome GET /users/1 (ポーリング) BEGIN TRANSACTION Minitest DB Rails SELECT GET /users/1 ROLLBACK create(:user) visit SELECT ブラウザ終了
Slide 75
Slide 75 text
Chrome GET /users/1 (ポーリング) BEGIN TRANSACTION Minitest DB Rails SELECT GET /users/1 ROLLBACK create(:user) visit SELECT ブラウザ終了
Slide 76
Slide 76 text
https://github.com/mtsmfm/rails/commit/1a197e4
Slide 77
Slide 77 text
GET /users/1 (ポーリング) BEGIN TRANSACTION Minitest DB Rails SELECT GET /users/1 ROLLBACK create(:user) visit SELECT ブラウザ終了 Chrome
Slide 78
Slide 78 text
https://github.com/rails/rails/pull/28223/files
Slide 79
Slide 79 text
➔ ブラウザの終了処理をDBクリーンアップより先にやらな いといけない ◆ Rails System Test はリリース前に直した ➔ 自分で database-cleaner など teardown 処理を入れて いる場合には、順番に注意 Assertion 後にサーバーエラー (対策)
Slide 80
Slide 80 text
何もしてないのに ログイン済み
Slide 81
Slide 81 text
➔ ログイン処理前なのにログインが済んでいることになって いる ➔ 直前のテストのセッションが残っている 何もしてないのにログイン済み
Slide 82
Slide 82 text
➔ ブラウザのクリーンアップは Cookie 削除してから blank ページを開く ➔ Cookie 削除前に開始したリクエストが、Cookie 削除後 に完了すると Cookie が消されない 何もしてないのにログイン済み (原因)
Slide 83
Slide 83 text
Minitest Chrome Cookie 削除 teardown Capybar a about:blank へ
Slide 84
Slide 84 text
Set-Cookie Minitest Chrome Rails GET Cookie 削除 teardown Capybar a about:blank へ
Slide 85
Slide 85 text
https://github.com/teamcapybara/capybara/blob/3.3.1/lib/capybara/selenium/driver.rb#L127
Slide 86
Slide 86 text
➔ blank ページにいる状態で全 cookie を消す 何もしてないのにログイン済み (対策)
Slide 87
Slide 87 text
そんな API はない
Slide 88
Slide 88 text
➔ 現在開いているドメイン以外の Cookie を消す方法がな い... ◆ モンキーパッチでお茶を濁しているが... 何もしてないのにログイン済み (対策) Capybara::Selenium::Driver.prepend(Module.new do def reset! quit end end)
Slide 89
Slide 89 text
Agenda | 01 Capybara の visit の向こう側 02 落ちる原因と対策 03 たまに落ちるテストと向き合う
Slide 90
Slide 90 text
03 たまに落ちるテストと向き合う
Slide 91
Slide 91 text
➔ 直すのめちゃくちゃめんどくさい ◆ デバッグが大変 ◆ 再現しない ➔ テストの書き方の問題やライブラリの問題なだけで、本番 影響がないことが多い ◆ いっそ無視 直しても費用対効果がよくない
Slide 92
Slide 92 text
➔ rspec-retry ◆ Retry 時のクリーンアップ処理まで考慮しきれず、 ちゃんと Retry できない場合も ➔ 既出かどうかのデータを集めて無視する? 無視する方法
Slide 93
Slide 93 text
https://ai.google/research/pubs/pub46593
Slide 94
Slide 94 text
➔ 420万あるテストの結果を集積しまくっている ➔ 16%はたまに落ちるテスト ➔ テスト結果が変化するとき、84%は不安定なテストによる もの Advances in Continuous Integration Testing at Google
Slide 95
Slide 95 text
➔ 過去に落ちたテスト一覧とか出してくれる ➔ ちょっと情報が足りない Circle CI
Slide 96
Slide 96 text
➔ テスト結果集積基盤を自前で作る? ◆ Quipper にはまだない ◆ 取り組みを教えて欲しい ➔ OSS 化の余地がありそう Future work
Slide 97
Slide 97 text
➔ Capybara とか DB とか ChromeDriver とかがどう絡み 合ってるか知ってると、何かおかしいときにアタリがつくの で覚えて損はない ◆ 別言語でも似たような構成にはなるはず ➔ だいたいの場合クリックに失敗してる ◆ わかっててもミスる ➔ たまに落ちるやつをうまいこと無視する仕組みが欲しい まとめ