Dec 17th 2017 by Kieran Eglin
You have a chat app that uses WebSockets for communication. There is a login system that dictates what socket you're subscribed to. With a normal system test, how would you ensure that messages are going back and forth in real time?
I'm assuming you're using Minitest and Rails 5 system tests with Capybara.
open_new_window! This will create a new browser tab that I can sign in and interact with. I can also switch between tabs with
On the surface, this may work. However, depending on how you subscribe users to your sockets, the act of opening a tab, signing out, signing in as a new user, then sending a message may cause a lot of fuss within your app. Also, doesn't this just feel innately wrong?
For those who don't know, you can create and switch between multiple Capybara sessions!
Creating a session will initialize a new window with completely separate cookies, localstorage, etc. This feels like a much cleaner and "more proper" solution. The default session name is, surprise,
:default. You can get and set these via
Here's and example of how you'd achieve this by using a pseudo-test:
test 'Chatroom messages update in real time' do login_as @bob visit '/chatroom' Capybara.session_name = :new_window # This can be named whatever you'd like # We are in an entirely new browser session! login_as @sue visit '/chatroom' submit_message 'Hey, Bob!' Capybara.session_name = :default # We are now back in Bob's session as we left it assert page.has_content?('Hey, Bob!')
That feels a lot better to me! It also works much more reliably.
However, it's extremely ugly to use this method
If you want to save yourself some typing and improve your code readability, I like to put this simple method in my
test_helper.rb or similar:
def within_session(name) old_session_name = Capybara.session_name Capybara.session_name = name.to_sym yield Capybara.session_name = old_session_name end
And it can simplify the above test to something like this:
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!')
Doesn't that look a lot better?
Finally, you may notice that the new browser windows remains open until your test suite finishes. This isn't really a problem, but I still don't like it. You can easily close windows with a method like this:
def close_session(name, original_session = :default) Capybara.session_name = name.to_sym page.driver.quit Capybara.session_name = original_session end
Which can be used like so when you're done with a window: