Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails 5: WebSocketのマルチセッションをMiniTestとシステムテストでテストする(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

Rails 5: WebSocketのマルチセッションをMiniTestとシステムテストでテストする(翻訳)

状況

やりとりにWebSocketを使うチャットアプリがあり、サブスクライブ先のソケットをディクテーションするためのログインシステムがあるとします。通常のシステムテストを使って、メッセージが往復していることを確認するにはどうしたらよいでしょうか?

MiniTestと、Capybaraを使うRails 5のシステムテストが前提です。

最初にやってみたこと

Capybaraのopen_new_windowを使って新しいブラウザタブを開き、そこでサインインして操作します。within_windowでタブ間を切り替えることもできます。

これは一見うまくいきそうに見えますが、何人かのユーザーがsocketにサブスクライブする方法によっては、タブのオープン/サインアウト/新しいユーザーとしてログインしてメッセージを送信するなどのアプリ操作がごちゃごちゃになってしまいそうです。それに、このやり方は本質的に何か間違っているのではないかと思えてきませんか。

今回の解決法

実はCabpybaraのセッションを複数作成して、それらを切り替えることができるのです。

セッションを1つ作成すると新規ウィンドウが1つ初期化され、cookieやローカルストレージなどが完全に他と分離されます。この方法はすっきりしていますし、「より正しい」ソリューションのように思えます。なお、このデフォルトセッション名は、よりによって:defaultです。セッション名の取得や設定はCapybara.session_nameで行えます。

以下はこれを行うためのテストコード例です。

test 'チャットルームのメッセージがリアルタイムで更新される' do
  login_as @bob
  visit '/chatroom'

  Capybara.session_name = :new_window # この名前はまったく自由に設定できます
  # ここからは完全に新しいブラウザセッションです!
  login_as @sue
  visit '/chatroom'

  submit_message 'Hey, Bob!'

  Capybara.session_name = :default # ここから元のBobのセッションに戻ります

  assert page.has_content?('Hey, Bob!')

私にはこっちの方がずっとよさそうに思えますし、実際しっかり動きます。

しかしこの書き方はいくらなんでも見苦しすぎます

もう少しコードを減らして読みやすくしたい場合、私はtest_helper.rbに置いている次のシンプルなメソッドみたいな方法が好きです。

def within_session(name)
  old_session_name = Capybara.session_name
  Capybara.session_name = name.to_sym

  yield

  Capybara.session_name = old_session_name
end

これを使って、先ほどのテストを以下のように簡潔に書けます。

test 'Chatroom messages update in real time' do
  login_as @bob
  visit '/chatroom'

  within_session :new_window do
    login_as @sue
    visit '/chatroom'

    submit_message 'Hey, Bob!'
  end

  assert page.has_content?('Hey, Bob!')

これでさらによくなったと思いませんか?

クリーンアップ

最後に、テストスイート実行完了後に新規ブラウザウィンドウが開きっぱなしになっていることがあります。問題というほとのものではありませんが、やはり好きになれません。以下のような方法でウィンドウを簡単に閉じられます。

def close_session(name, original_session = :default)
  Capybara.session_name = name.to_sym

  page.driver.quit

  Capybara.session_name = original_session
end

次のように、テストの終わったウィンドウに対して使うこともできます。

close_session :new_window

Happy coding!

関連記事

TestProf: Ruby/Railsの遅いテストを診断するgem(翻訳)

Rails: システムテストをRSpecで実行する(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。