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
Rails Assets Pipeline "Cannot Allocate Memory - Nodejs"
How to Add a Virtual Attribute to a Model in Ruby on Rails
Any Way to Determine Which Object Called a Method
Importing CSV Quoting Error Is Driving Me Nuts
Printing an Ascii Spinning "Cursor" in the Console
Properly Converting a Cmyk Image to Rgb with Rmagick
Prepend a Single Line to File with Ruby
Sidekiq Not Deallocating Memory After Workers Have Finished
How to Backreference in Ruby Regular Expression (Regex) with Gsub When I Use Grouping
Rmagick Remove White Background from Image and Make It Transparent
Ruby: How to Make Irb Print Structure for Arrays and Hashes
Changing Field Separator/Delimiter in Exported CSV Using Ruby CSV