可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
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 ...
回答2:
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
.
回答3:
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.
回答4:
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)
回答5:
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.