I'm using TestUnit and I want to test the remember me functionality (when user login).
the cookies variable(and also require/response .cookies) contain only the cookie value without the expire time.
Rails somehow tell the web-browser when the cookie should be expire, so I assume there must be a way to check the cookie expire time.
test "set permanent cookie" do
post :create, email: 'email', password: 'password', remember_me: true
# cookies[:auth_token] = random_string
# @request.cookies[:auth_token] = also_random_string
# @response.cookies[:auth_token] = also_random_string
end
the problem is that I can get only the values of the cookies and not the hash that contain the expire time.
As you've noticed, the cookies
Hash only contains the values, not the expiration times, when you inspect it after your post
call (this has been the behavior since at least Rails 2.3).
You have two options:
First, you could inspect the response.headers["Set-Cookie"]
item instead. It will include the expiration time in there. However, the Set-Cookie
value is just a single string, which you would need to then parse. For example, cookies["foo"] = {:value => "bar", :expires => Time.now + 10.years }
would give you:
response.headers["Set-Cookie"]
# => "foo=bar; path=/; expires=Mon, 16-Aug-2021 21:54:30 GMT"
The other option (taken from This Question/Answer), would be to stub the cookie jar and make sure it is sent an expires
value:
stub_cookie_jar = HashWithIndifferentAccess.new
controller.stub(:cookies) { stub_cookie_jar }
post :create, email: 'email', password: 'password', remember_me: true
expiring_cookie = stub_cookie_jar['expiring_cookie']
expiring_cookie[:expires].to_i.should be_within(1).of(1.hour.from_now.to_i)
Unfortunately, I couldn't get the solutions presented or linked in @DylanMarkow's answer working, so here is how I tested that a "permanent" cookie was being set when a Remember Me checkbox was checked (the tests are influenced/blatantly copied from the Test::Unit tests that DHH made in the commit that added cookies.permanent
to Rails).
Tests use RSpec and FactoryGirl.
spec/requests/authentication_requests_spec.rb
require 'spec_helper'
describe "Authentication Requests" do
# ...
describe "authorization" do
# ...
describe "cookies" do
let(:user) { FactoryGirl.create(:user) }
subject { response.headers["Set-Cookie"] }
context "when remember me is set" do
before { sign_in_request(user) }
it { should =~ %r(.+expires.+#{20.years.from_now.year}) }
end
context "when remember me is not set" do
before { sign_in_request(user, remember_me: false) }
it { should_not =~ %r(expires) }
end
end
end
end
spec/utilities.rb
def sign_in_request(user, remember_me: "true")
post session_path(
session: {
email: user.email,
password: user.password,
remember_me: remember_me
}
)
end
App code snippets below use i18n, Haml, Bootstrap, and Simple Form syntax:
app/views/sessions/new.html.haml
= simple_form_for :session, url: session_path, html: {class: 'form-vertical' } do |f|
= f.input :email
= f.input :password
.checkbox
= f.input :remember_me, as: :boolean, label: false do
= check_box_tag :remember_me, 1, true
= label_tag :remember_me
= f.submit t('.signin'), class: "btn btn-large btn-primary"
app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
# ...
def create
if user = User.authenticate(params[:session][:email],
params[:session][:password])
sign_in user
flash[:success] = t('flash.successful_signin')
redirect_to root_url
else
flash.now[:error] = t('flash.invalid_credentials')
render 'new'
end
end
# ...
end
app/models/user.rb
class User < ActiveRecord::Base
has_secure_password
# ...
before_create :generate_authentication_token
def self.authenticate(email, password)
find_by_email(email).try(:authenticate, password)
end
private
def generate_authentication_token
begin
self.authentication_token = SecureRandom.urlsafe_base64
end while User.exists?(authentication_token: self.authentication_token)
end
end
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# ...
private
# A cookie that does not have an expiry will automatically be expired by
# the browser when browser's session is finished.
# cookies.permanent sets the expiry to 20 years
# Booleans seem to get passed from forms as strings
def sign_in(user)
if remember_me
cookies.permanent[:authentication_token] = user.authentication_token
else
cookies[:authentication_token] = user.authentication_token
end
self.current_user = user
end
helper_method :sign_in
def remember_me
params[:session].try(:[], :remember_me) == "true" ||
params[:remember_me] == "true"
end
# ...
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With