Given I Am Signed in Using capybara-webkit

Inspired by the blog post Capybara, Cucumber and How the Cookie Crumbles, I wanted to write a Cucumber step that signs a user directly into my application, without the need to navigate to a log-in page and submit a form. I am not thrilled about the stubbing and monkey patching discussed in that post, and I am also not concerned about doing something that is specific to the capybara-webkit driver used on my current project. The result is a capybara-webkit specific way of bypassing authentication from my Cucumber features.

The previously mentioned blog post sets a cookie with a token that identifies the logged in user. I decided to simply use the user’s ID instead:

  def current_user
    return @current_user if defined?(@current_user)
 
    if Rails.env.test? && cookies[:test_user_id]
      @current_user = User.find(cookies[:test_user_id])
    end
  end

Allowing a production user to be automatically signed into the application just by having their user ID in a cookie is an exceedingly poor idea; thus, the check to only allow this type of login to work in the test environment.

Now all the Cucumber step needs to do is to set a cookie with the appropriate user ID:

  When /^I am signed in as "([^"]*)"$/ do |email|
    user_id = User.find_by_email!(email).id
    page.driver.browser.set_cookie(
      "test_user_id=#{user_id}; path=/; domain=localhost")
  end

Now when the next request is made from the test, the specified user will be set in the current_user call.

Notes

  • This particular test suite is configured to access the web server using http://localhost:8888. I first tried setting the domain in the cookie to be 127.0.0.1, but it was not working. I am not sure what the default Capybara configuration is, but if you run into issues try both 127.0.0.1 and localhost as the domain in the cookie.
  • In addition to the user ID I also need to fake out several more cookies that the application sets when a user signs in using the normal form. I ran into some trouble setting these until I escaped the values with CGI.escape:

  page.driver.browser.set_cookie(
    "#{key}=#{CGI.escape(value.to_s)}; path=/; domain=localhost")

  • The version of capybara-webkit I am using is a little older; it appears that a new interface for setting/getting cookies has been added in the form of a cookies method on the Capybara::Driver::Webkit class. I haven’t used this interface yet so I can’t speak to how it may differ the approach described above.