Turboを使ったRailsのシステムテストではJavaScriptの処理完了のタイミングに注意

test-unitでのシステムテストでちょっとハマったのでメモ。

症状

テスト実行時にログイン処理が不安定(ログインできる/できない)という症状が発生。

class BooksTest < ApplicationSystemTestCase
  setup do
    @book = books(:one)

    # ログイン処理
    visit root_url
    fill_in 'Eメール', with: 'alice@example.com'
    fill_in 'パスワード', with: 'password'
    click_on 'ログイン'
  end

  test 'visiting the index' do
    visit books_url 
    assert_selector 'h1', text: 'Books' # ここでテストがパスしない
  end
end

原因

Railsアプリが描画処理にTurboを使用しており、JavaScriptの描画処理(画面遷移)の完了を待たずにCapybaraが動作してしまうため。

  • テスト検証のタイミングに描画処理の完了が間に合っていない(テストのフライング)
  • Turboに限らず、Ajaxでの非同期処理でも発生するらしい
  • ブラウザによってはパスすることもあるっぽい

対策

assert_xxx 系のメソッドでJavaScriptの描画処理の完了を待たせる。

  • assert_textassert_selector等が使える
  • デフォルトで最大2秒待ってくれる
class BooksTest < ApplicationSystemTestCase
  setup do
    @book = books(:one)

    # ログイン処理
    visit root_url
    fill_in 'Eメール', with: 'alice@example.com'
    fill_in 'パスワード', with: 'password'
    click_on 'ログイン'
    assert_text 'ログインしました。' # JavaScriptの処理完了を待たせる
  end
  
  test 'visiting the index' do
    visit books_url
    assert_selector 'h1', text: 'Books' # JavaScriptの処理完了後に実行される
  end
end

その他の対策

  • sleep を使って処理を待たせる
    • テスト実行が遅くなるので微妙かも...
  • Turboの無効化
    • アプリ自体の描画処理が遅くなるっぽい
    • React、Vue等では対策できない

感想

久々に時間溶かしてしまった... システムテストは描画処理の完了と検証開始のタイミングは必ずしも一致していないことに注意していこう。