Capybara を使って PhantomJS や Selenium などブラウザを介した E2E テストをするときに遭遇する、たまに落ちるテストのパターンをまとめていく。
ケース1: 押し損ね
落ちたときの状況
ボタンやリンクのクリックに失敗してそうに見える。
原因
click_on
などはクリックするときに対象の要素を見つけ、 x 座標と y 座標を取得した上でその座標をクリックする。
対象の要素を見つけたときとクリックする瞬間の座標が異なるため、クリックはしているが目標の要素をクリックはできていない。
ずれる理由の多くは画像などの読み込みに伴う座標のずれ。
アニメーションもずれ得るが自分で把握して適宜待つようにしているパターンが多いのでだいたいの場合問題にはなっていない。
対策
読み込みタイミングによらず座標が固定されるような CSS を書くのが理想的。
回線によってはユーザもボタンを押そうと思ったときに座標がずれているということを意味しているからだ。
一方、全部見直すのは大変なので画像の読み込みタイミングによるずれが怪しければ適時こんな感じのヘルパを使うとよい。
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"), function (e) { return e.complete; })
JS
end
end
ケース2: なぜかサーバ側でエラー
落ちたときの状況
200 を返すはずがなぜかサーバ側でエラーになる。
テスト中で用意したデータが消えているとエラーになりそう。
原因
ajax などでブラウザがリクエストを投げている最中にも関わらずテストは完了してしまい、サーバがリクエストを処理する前に teardown/after に入って DB のクリーンアップなどを行ってしまっている。
対策
teardown/after の早い段階で ajax やサーバの処理完了を待つ。
HTTP リクエストを投げるフレームワークやライブラリに合わせた evaluate_script
をすればよい。
このあたりは The One True Guide to database transactions with Capybara が詳しい。
待ち方は transactional_capybara が参考になる。
一方、テスト用に rack middleware を挟み込んで mutex を使ってちゃんと待つ方法があるのではと思って現在研究中。
よく考えると、ブラウザが終了するまでクリーンアップをしなければいいのだ。
ケース3: ログインされっぱなし
落ちたときの状況
なぜか次のテストに行ってもログインされっぱなし
原因
Capybara.reset_sessions!
は
- cookie を消す
- about:blank を表示する
という流れで処理をするが、 1 と 2 の間でリクエストを処理してしまって cookie を覚えていることがある。
対策
teardown で page.driver.quit
して丸ごと初期化する。
about:blank にいるときに cookie を全消しできればいいのだが、
Selenium は現在開いているドメインの cookie しか消せない。