18

I have a action in some controller that set some value in a permanent signed cookie like this:


def some_action
    cookies.permanent.signed[:cookie_name] = "somevalue"
end

And in some functional test, I'm trying to test if the cookie was set correctly suing this:


test "test cookies" do
    assert_equal "somevalue", cookies.permanent.signed[:cookie_name]
end


However, when I run the test, I got the following error:


NoMethodError: undefined method `permanent' for #

If I try only:


test "test cookies" do
    assert_equal "somevalue", cookies.signed[:cookie_name]
end


I get:


NoMethodError: undefined method `signed' for #

How to test signed cookies in Rails 3?

Joao Pereira
  • 2,454
  • 4
  • 27
  • 35

5 Answers5

18

I came across this question while Googling for a solution to a similar issue, so I'll post here. I was hoping to set a signed cookie in Rspec before testing a controller action. The following worked:

jar = ActionDispatch::Cookies::CookieJar.build(@request)
jar.signed[:some_key] = "some value"
@request.cookies['some_key'] = jar[:some_key]
get :show ...

Note that the following didn't work:

# didn't work; the controller didn't see the signed cookie
@request.cookie_jar.signed[:some_key] = "some value"
get :show ...
balexand
  • 9,549
  • 7
  • 41
  • 36
  • Just ran across this and it solve my problem perfectly, but as an important addendum, I had to change: @request.cookies[:some_key] to: @request.cookies["some_key"] – Atiaxi Sep 09 '11 at 11:03
  • Great balexand. I wish you I could give you 10 thumbs-ups. – allesklar Dec 08 '11 at 15:29
  • @Atiaxi, using Rails 3.2.12 and I could use the symbol to access the @request.cookies[:some_key] = jar[:some_key] – Gerry Shaw Feb 28 '13 at 21:42
  • the original question was about looking into the response cookies. not the request cookies. – Mathieu J. Feb 11 '14 at 16:01
  • Rspec explains Strings vs Symbols when referencing cookies here: https://www.relishapp.com/rspec/rspec-rails/docs/controller-specs/cookies – Chris Beck Dec 29 '14 at 17:01
8

In rails 3's ActionControlller::TestCase, you can set signed permanent cookies in the request object like so -

 @request.cookies.permanent.signed[:foo] = "bar"

And the returned signed cookies from an action taken in a controller can be tested by doing this

 test "do something" do
     get :index # or whatever
     jar = @request.cookie_jar
     jar.signed[:foo] = "bar"
     assert_equal jar[:foo], @response.cookies['foo'] #should both be some enc of 'bar'
 end 

Note that we need to set signed cookie jar.signed[:foo], but read unsigned cookie jar[:foo]. Only then we get the encrypted value of cookie, needed for comparison in assert_equal.

szeryf
  • 3,197
  • 3
  • 27
  • 28
Anthony Bishopric
  • 1,306
  • 11
  • 23
7

After looking at the Rails code that handles this I created a test helper for this:

  def cookies_signed(name, opts={})
    verifier = ActiveSupport::MessageVerifier.new(request.env["action_dispatch.secret_token".freeze])
    if opts[:value]
      @request.cookies[name] = verifier.generate(opts[:value])
    else
      verifier.verify(cookies[name])
    end
  end

Add this to test_help.rb, then you can set a signed cookie with:

cookies_signed(:foo, :value => 'bar')

And read it with:

cookies_signed(:foo)

A bit hackish maybe, but it does the job for me.

Roger Ertesvag
  • 1,784
  • 2
  • 14
  • 15
  • Nicely done, I've renamed the method to #signed_cookie, and slapped it into a module for mixing into controller specs. [See this gist](https://gist.github.com/2694930) for a reference – Kenneth Kalmer May 14 '12 at 16:30
2

The problem (at least on the surface) is that in the context of a functional test (ActionController::TestCase), the "cookies" object is a Hash, whereas when you work with the controllers, it's a ActionDispatch::Cookies::CookieJar object. So we need to convert it to a CookieJar object so that we can use the "signed" method on it to convert it to a SignedCookieJar.

You can put the following into your functional tests (after a get request) to convert cookies from a Hash to a CookieJar object

@request.cookies.merge!(cookies)
cookies = ActionDispatch::Cookies::CookieJar.build(@request)
Polemarch
  • 1,646
  • 14
  • 10
  • I have the exact same problem. I'm using Test unit. I don't understand how to use your 2-liner. I tried different permutations but nothing worked. Can you please give a more thorough example on how to use this. – allesklar Mar 03 '11 at 08:22
0

The problem also appears to be your tests.

Here is some code and tests I used to TDD the situation where you want to set a cookie's value from passing a params value into a view.

Functional Test:

test "reference get set in cookie when visiting the site" do
  get :index, {:reference => "121212"}
  refute_nil cookies["reference"]
end

SomeController:

before_filter :get_reference_code

ApplicationController:

def get_reference_code
  cookies.signed[:reference] ||= params[:reference]
end

Notice that the refute_nil line, the cookies is a string... that is one thing that also made this test not pass, was putting a symbol in cookies[:reference] the test did not like that, so i didn't do that.

pjammer
  • 9,489
  • 5
  • 46
  • 56