Reputation: 8894
There is a lot of confusion about setting cookies in rspec http://relishapp.com/rspec/rspec-rails/v/2-6/dir/controller-specs/file/cookies
in your controller, normally you can write
cookies['transaction_code'] = { :expires => 300.seconds.from_now, :value => c }
but in rspec i can only write
request.cookies['transaction_code'] = transaction_code
if i say
request.cookies['transaction_code'] = { :expires => 300.seconds.from_now, :value => c }
i get the hash back as value of cookies['transaction_code'] in my controller.
Now my question is: how do i set/test cookie expiry then in an rspec controller test example?
UPDATE: On seconds thought: What i mean is: how do i test if the controller is reacts to an expired cookie as expected, but in fact an expired cookie is just like no cookie if i trust cookie implementation, which i should do, so after all maybe my question makes no sense. If this is the case, i need to test if (another) controller action sets an expiring cookie correctly, but how do i do it if cookies['transaction_code'] in the test only gives the value back?
Upvotes: 16
Views: 8121
Reputation: 6287
If you want to check several cookie attributes at once, inspect the Set-Cookie header, something like this:
it 'expires cookie in 15 minutes' do
travel_to(Date.new(2016, 10, 25))
post 'favorites', params: { flavor: 'chocolate' }
travel_back
details = 'favorite=chocolate; path=/; expires=Tue, 25 Oct 2016 07:15:00 GMT; HttpOnly'
expect(response.header['Set-Cookie']).to eq details
end
This is a bit brittle, in that other, non-critical attributes of the cookie may break this string. But it does keep you out of Rails internals, and lets you check several attributes at once.
A better way is to match just one attribute at a time, this is a much more robust method that is not so vulnerable to changes in how the Set-Cookie string is constructed, or what other attributes you might add to the cookie in the future:
expect(response.header['Set-Cookie']).to match(
/favorite=chocolate.*; expires=Tue, 25 Oct 2016 07:15:00 GMT/
)
Upvotes: 0
Reputation: 2883
I came from the future with this:
it 'sets the cookie expiration' do
stub_cookie_jar = HashWithIndifferentAccess.new
allow(controller).to receive(:cookies).and_return(stub_cookie_jar)
get :index
expiracy_date = stub_cookie_jar[:expires]
expect(expiracy_date).to be_between(1.hour.from_now - 1.minutes,
1.hour.from_now)
end
Upvotes: 2
Reputation: 164
Another option using Timecop:
Timecop.freeze(Time.now)
expect(controller.send(:cookies)).to receive(:[]=).with('cookie_name',
value: 'whatever',
expires: 1.hour.from_now
)
get :index
Upvotes: 1
Reputation: 1320
Browsers do not send cookie attributes back to the server. This is why you can only send the key-value pair to the action.
Since you can assume that Rails, Rack and browsers do the right thing with the arguments, all you really need to test is the arguments your code is passing to the CookieJar
.
To test that expiry is being set properly in the controller setting the cookie, you could stub out the #cookies
method and make sure the right settings are being passed to it.
# app/controllers/widget_controller.rb
...
def index
cookies[:expiring_cookie] = { :value => 'All that we see or seem...',
:expires => 1.hour.from_now }
end
...
# spec/controllers/widget_controller_spec.rb
...
it "sets the cookie" do
get :index
response.cookies['expiring_cookie'].should eq('All that we see or seem...')
# is but a dream within a dream.
# - Edgar Allan Poe
end
it "sets the cookie expiration" do
stub_cookie_jar = HashWithIndifferentAccess.new
controller.stub(:cookies) { stub_cookie_jar }
get :index
expiring_cookie = stub_cookie_jar['expiring_cookie']
expiring_cookie[:expires].to_i.should be_within(1).of(1.hour.from_now.to_i)
end
...
Testing much more than this is boiling the ocean. At some point, you have to assume that the stack you are sitting on (e.g., Rails, Rack, the web server, TCP/IP, OS, web browsers, etc) works properly and focus on the code you control.
Upvotes: 18
Reputation: 1
set request.cookies['transaction_code'] as a CGI::Cookie in your spec. http://www.ruby-doc.org/stdlib/libdoc/cgi/rdoc/classes/CGI/Cookie.html#M000169
Upvotes: 0