なぜE2Eテストがたまに落ちるのか / How do E2E tests fail randomly
by
Fumiaki MATSUSHIMA
×
Copy
Open
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
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 とかがどう絡み 合ってるか知ってると、何かおかしいときにアタリがつくの で覚えて損はない ◆ 別言語でも似たような構成にはなるはず ➔ だいたいの場合クリックに失敗してる ◆ わかっててもミスる ➔ たまに落ちるやつをうまいこと無視する仕組みが欲しい まとめ