Using Cookies with Rack::Test

Using Cookies with Rack::Test

Rack::Test keeps a cookie jar that persists over requests. You can access it with rack_mock_session.cookies. Let's say you have a handler like this:

get '/cookie/set' do
response.set_cookie "foo", :value => "bar"
end

Now you could test it with something like this:

it 'defines a cookie' do
get '/'
rack_mock_session.cookie_jar["foo"].should == "bar"
end

You can also access cookies with last_request.cookies, but as the name says, it contains the cookies for the last request, not the response. You can set cookies with set_cookie and clear them with clear_cookies.

it 'shows how to set a cookie' do
clear_cookies
set_cookie "foo=quux"
get '/'
last_request.cookies.should == {"foo" => "quux"}
end

Update: If you want the cookie jar to persist across the test cases (it blocks), you need to initialize the Rack session before executing any test cases. To do so, add this before hook to your describe block.

before :all do
clear_cookies
end

Alternative, you could for example use before :each to set up the necessary cookies before each request.

Rspec + Capybara + Rack::Test — disable cookies

There is no built-in way to disable cookies when using Rack::Test. You can clear them during a test with

page.driver.browser.clear_cookies

which may provide the functionality you need. If not, you can install middleware during your test runs and enable/disable the stripping of cookies on every request. You can see an example of that at https://makandracards.com/makandra/15187-how-to-disable-cookies-in-cucumber-tests. The example is for cucumber but should be easy enough to convert to just plain RSpec.

Setup cookie.signed in Rails 5 controller integration tests

This seems to be a known bug in Rails 5 and above (the linked issue is about cookies.encrypted but the same applies to cookies.signed). The problem is that in controller tests, the cookie jar is a Rack::Test::CookieJar class instance which does not support signed / encrypted cookies. On the other hand, in the application itself, the cookie jar is a ActionDispatch::Cookies::CookieJar class instance which supports both these special cookie types.

Nevertheless, to just construct a signed cookie in your controller test, you can manually create an ActionDispatch request cookie jar and use that to retrieve the signed cookie value:

# app/test/controllers/foo_test.rb

require 'test_helper'

class FooControllerTest < ActionDispatch::IntegrationTest
test 'should be working' do
my_cookies = ActionDispatch::Request.new(Rails.application.env_config.deep_dup).cookie_jar
my_cookies.signed[:token] = '7e5201169ef160e31058d2a1976a5552'

cookies[:token] = my_cookies[:token]
get '/foobar/123'
end
end

The first test line creates a new ActionDispatch request with the application requests default environment settings (they define e.g. the secret used for signing cookies) and returns it's cookie jar. Then you simply set the :token signed cookie for the desired value (this cookie jar does have the signed method defined as this is the ActionDispatch::Cookies::CookieJar, not Rack::Test::CookieJar). Finally, you retrieve the signed cookie value by accessing it without the signed accessor and set the same-named test cookie using this value.

Now, when the test reaches the controller code, the controller should see the proper value in the cookies.signed[:token] cookie.

Rails 5 - integration tests NoMethodError: undefined method `signed' for # Rack::Test::CookieJar:0x00000006796390

Well, none of those modifications solved my problem to be honest, some of my tests started to behave inconsistently.

I've decided to thumb through Ruby On Rails Tutorial and discovered that instead of trying to replace the default cookie jar in integration test I can simply check cookies in helper test. It turns out that in tests for helpers the cookie jar is not Rack::Test::CookieJar but ActionDispatch::Cookies::CookieJar which is fine and works with signed method. For integration tests I simply rely on modified logged_in? method which tests only session[:user_id] and looks like this:

class ActiveSupport::TestCase
def logged_in?
!session[:user_id].nil?
end
end

To ensure that cookies works, I created a suitable test for SessionsHelper which can be found in rails tutorial.

What is also important is that I underestimated session. I've decided to store user's id there and treat it as a main indicator that the user is logged in, thereby using cookies only for remembering users. In this way I don't have to worry about testing cookies in integration tests which seems to be faulty.

Big thanks to OnlySteveH who helped me out by asking some useful questions.

How do I set a cookie for an integration test in Rails?

For Rails 5 integration tests metkat's answer should be altered slightly:

get "/", headers: {"HTTP_COOKIE" => "legal_accepted=yes; cookie2=value2;"}


Related Topics



Leave a reply



Submit