Setup cookie.signed in Rails 5 controller integration tests
cookies, ruby-on-rails, ruby-on-rails-5, unit-testing
Solution
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.
Problem
Imagine the scenario that there is a controller integration test calls a controller method, in which `cookie.signed` is used for some integrity checking. 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 `cookie.signed` setup in the test. The test code above throws an exception: NoMethodError: undefined method `signed’ for Rack::Test::CookieJar:0x007fe90965ccd8 Tried to search for a solution, but the closest I could find was this article, https://sikac.hu/reconstruct-a-cookie-jar-and-read-signed-cookie-in-capybara-f71df387f9ff, but couldn't figure out how to construct `ActionDispatch::Request` object.