Setting cookie.signed in Rails 5 controller integration tests
Imagine a script that contains a controller integration test calls a controller method that is cookie.signed
used to do some kind of integrity check.
controller
# app/controllers/foo_controller.rb
def index
entity = FooEntity.find_by_id(params[:id])
if entity.nil?
raise ActionController::BadRequest, 'Could not find requested entity.'
else
@is_authorized = entity.token == cookies.signed[:token]
if @is_authorized
# Success! The path to be tested.
else
raise ActionController::BadRequest, 'Unauthorized cookie token.'
end
end
end
Controller test
# app/test/controllers/foo_test.rb
require 'test_helper'
class FooControllerTest < ActionDispatch::IntegrationTest
test 'should be working' do
cookies.signed[:token] = '7e5201169ef160e31058d2a1976a5552'
get '/foobar/123'
end
end
However, I'm not sure how to get the installation cookie.signed
in the test. The above test code throws an exception:
NoMethodError: undefined method `signed for Rack :: Test :: CookieJar: 0x007fe90965ccd8
Tried to find a solution but the closest one I could find this article, https://sikac.hu/reconstruct-a-cookie-jar-and-read-signed-cookie-in-capybara-f71df387f9ff but couldn't figure out how to build the object ActionDispatch::Request
.
source to share
This appears to be a known bug in Rails 5 and up (a related issue - but the same applies to ). The problem is, in controller tests, the cookie heap is an instance of a class that doesn't support signed / encrypted cookies . On the other hand, in the application itself, a cookie jar is an instance of a class that supports both of these special types of cookies. cookies.encrypted
cookies.signed
Rack::Test::CookieJar
ActionDispatch::Cookies::CookieJar
However, to simply create a signed cookie in your controller test, you can manually create a chunk of the request cookie ActionDispatch
and use to get the value of the signed cookie:
# 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 request ActionDispatch
with the default application environment settings (these define, for example, the secret used to sign cookies) and returns its cookie jar. Then you just set the :token
signed cookie to the value you want (this cookie jar has a method signed
defined as ActionDispatch::Cookies::CookieJar
, not Rack::Test::CookieJar
). Finally, you will load the signed cookie value by accessing it without an accessory signed
and set the same test cookie name using that value.
Now when the test reaches the controller code, the controller should see the correct value in the cookie cookies.signed[:token]
.
source to share