別々のブラウザから複数のユーザがログインするような状況を再現したintegration testを実装しようとするときなど、 ユーザごとにセッションが必要になるなどして、 1つのテスト内で複数のセッションが必要になることがあります。 Ruby on Railsのintegration testでは、 標準でこのような複数のセッションを用いるテストに対応しており、 公式ドキュメントの中では、[ActionDispatch::IntegrationTestのAPIドキュメント](https://api.rubyonrails.org/v7.2.1/classes/ActionDispatch/IntegrationTest.html)に説明があります。 ## 複数のセッションを用いたintegration testの例 公式情報は上述のAPIドキュメントをご覧いただければと思いますが、一応こちらでも簡単な例を使ってintegration testで複数のセッションを用いる方法をご紹介します。 ここではテスト内容として、ログインしているユーザが列挙されるページをテストすることを想定してテストコードを考えます。 具体的には、ユーザ1がログインするとこのページにユーザ1が現れ、 続けてユーザ2がログインすると今度はユーザ1とユーザ2の両方がページに現れることを確認します。 ### シンプルにopen_session()でセッションを作って用いる場合の例 複数のセッションを用いる場合に鍵となるメソッドは、open_session()です。 このメソッドはセッションのオブジェクト返しますので、必要なセッションの回数分呼び出せば、必要な数のセッションが作成出来ます。 セッションを複数作成したら、各セッションのオブジェクトのインスタンスメソッドとしてget()やassert_response()を呼び出すことで、 セッションを指定してアクションやアサーションを実行できます。 以下にサンプルコードを示します。 ```ruby require "test_helper" class MultipleSessionTest < ActionDispatch::IntegrationTest test "login users page" do user1 = users(:one) user2 = users(:two) sess1 = open_session sess2 = open_session sess1.get login_path sess1.assert_response :success sess1.post login_path, params: { {email: user1.email, password: user1.password} } sess1.assert_response :found sess1.assert_redirected_to root_path sess1.follow_redirect! sess1.get login_users_path sess1.assert_response :success assert_match /@#{user1.username}/, sess1.response.body assert_no_match /@#{user2.username}/, sess1.response.body sess2.get login_path sess2.assert_response :success sess2.post login_path, params: { {email: user2.email, password: user2.password} } sess2.assert_response :found sess2.assert_redirected_to root_path sess2.follow_redirect! sess1.get login_users_path sess1.assert_response :success assert_match /@#{user1.username}/, sess1.response.body assert_match /@#{user2.username}/, sess1.response.body end end ``` ### 繰り返される処理をDSLのメソッドにまとめる例 上述のシンプルな例を見てみると、ログイン処理などで、対象とするセッションは異なるものの、ほぼ同じ処理が繰り返されていることが分かります。 こういった繰り返される処理は、特定のテストで用いるセッションのDSL (Domain-Specific Language) としてまとめることが出来ます。 以下のテストコードは、テストの手順や内容は上述のシンプルな例と同じですが、今度はこのDSLを用いてコードの繰り返しを排除したものになっています。なお、テストコードの読みやすさの観点でも、こちらの例の方が良くなっているのではないでしょうか。 ```ruby require "test_helper" class MultipleSessionWithDSLTest < ActionDispatch::IntegrationTest test "login users page" do user1 = users(:one) user2 = users(:two) sess1 = open_session_with_dsl sess2 = open_session_with_dsl sess1.login(user1) sess1.get_login_users assert_match /@#{user1.username}/, sess1.response.body assert_no_match /@#{user2.username}/, sess1.response.body sess2.login(user2) sess1.get_login_users assert_match /@#{user1.username}/, sess1.response.body assert_match /@#{user2.username}/, sess1.response.body end private module CustomDsl def login(user) get login_path assert_response :success post login_path, params: { {email: user.email, password: user.password} } assert_response :found assert_redirected_to root_path follow_redirect! end def get_login_users get login_users_path assert_response :success end end def open_session_with_dsl open_session do |sess| sess.extend(CustomDsl) end end end ```