I'm trying to check if an administrator is signed out in an Rspec test. However the usual signed_in? method can't be seen from rspec and isn't part of the RSpec Devise Helpers.
Something like this is what i have in place
before (:each) do
#admin = FactoryGirl.create(:administrator)
sign_in #admin
end
it "should allow the admin to sign out" do
sign_out #admin
##admin.should be_nil
##admin.signed_in?.should be_false
administrator_signed_in?.should be_false
end
Is there anothe way to check the session of the administrator and see if he's actually signed in or not?
it "should have a current_user" do
subject.current_user.should_not be_nil
end
Found at https://github.com/plataformatec/devise/wiki/How-To:-Controllers-and-Views-tests-with-Rails-3-%28and-rspec%29
I think it's really what you need How To: Test controllers with Rails 3 and 4 (and RSpec)
Just check current_user. It should be nil
Add. Good practice is using syntax like this
-> { sign_out #admin }.should change { current_user }.from(#admin).to(nil)
Not a new answer, really, but my rep isn't high enough to comment...:
If you've already overridden subject, the controller is available as controller in controller specs, so:
expect { ... }.to change { controller.current_user }.to nil
To check for a specific user, say generated by FactoryGirl, we've had good success with:
let(:user) do FactoryGirl.create(:client) ; end
...
it 'signs them in' do
expect { whatever }.to change { controller.current_user }.to user
end
it 'signs them out' do
expect { whatever }.to change { controller.current_user }.to nil
end
it "signs user in and out" do
user = User.create!(email: "user#example.org", password: "very-secret")
sign_in user
expect(controller.current_user).to eq(user)
sign_out user
expect(controller.current_user).to be_nil
end
You can refer this link devise spec helper link
Related
I've spent far too long messing with this before asking for help. I can't seem to get RSpec and Sorcery to play together nicely. I've read through the docs on Integration testing with Sorcery and can post the login action properly, but my tests still doesn't think the user is logged in.
# spec/controllers/user_controller_spec
describe 'user access' do
let (:user) { create(:user) }
before :each do
login_user(user[:email], user[:password])
end
it "should log in the user" do
controller.should be_logged_in
end
end
And my login_user method
# spec/support/sorcery_login
module Sorcery
module TestHelpers
module Rails
def login_user email, password
page.driver.post(sessions_path, { email: email , password: password, remember_me: false })
end
end
end
end
The sessions controller handles the pages properly when I use them on the generated pages just fine. I tried outputting the results of the login_user method and it appears to properly post the data. How do I persist this logged in user through the tests? Does a before :each block not work for this? I'm just not sure where it could be running wrong and I'm pretty new to testing/RSpec so I may be missing something obvious. I'd appreciate any help.
Here's the output of the failed tests:
1) UsersController user access should log in the user
Failure/Error: controller.should be_logged_in
expected logged_in? to return true, got false
I just went through this yesterday. Here's what I did, if it helps.
Sorcery provides a test helper login_user that relies on a #controller object being available. This works great in controller specs, but doesn't work in integration tests. So the workaround in integration tests is to write another method (like the one you have above) to simulate actually logging in via an HTTP request (essentially simulating submitting a form).
So my first thought is that you should try renaming your method to login_user_post or something else that doesn't collide with the built-in test helper.
Another potential gotcha is that it looks to me like the Sorcery helper assumes that your user's password is 'secret'.
Here's a link to the built-in helper so you can see what I'm talking about:
https://github.com/NoamB/sorcery/blob/master/lib/sorcery/test_helpers/rails.rb
Good luck - I really like this gem except for this part. It is really only fully explained by patching together SO posts. Here's the code I use:
Integration Helper
module Sorcery
module TestHelpers
module Rails
def login_user_post(user, password)
page.driver.post(sessions_url, { username: user, password: password})
end
def logout_user_get
page.driver.get(logout_url)
end
end
end
end
Integration Spec (where user needs to be logged in to do stuff)
before(:each) do
#user = create(:user)
login_user_post(#user.username, 'secret')
end
Controller Spec (where the regular login_user helper works fine)
before(:each) do
#user = create(:user)
login_user
end
Note that login_user doesn't need any arguments if you have an #user object with the password 'secret'.
Did you try adding to spec/spec_helpers.
RSpec.configure do |config|
# ...
config.include Sorcery::TestHelpers::Rails::Controller
end
Nota that you need to include Sorcery::TestHelpers::Rails::Controller, not just Sorcery::TestHelpers::Rails.
Then you will be able to login_user from any controller specs like:
describe CategoriesController do
before do
#user = FactoryGirl::create(:user)
end
describe "GET 'index'" do
it "returns http success" do
login_user
get 'index'
expect(response).to be_success
end
end
end
The way you pass a password is probably wrong. It may be encrypted at this point. In provided example I will try to do this at first:
describe 'user access' do
let (:user) { create(:user, password: 'secret') }
before :each do
login_user(user[:email], 'secret')
end
it "should log in the user" do
controller.should be_logged_in
end
end
This seems to be very poorly documented. The above solutions did not work for me. Here's how I got it to work:
Check your sessions_url. Make sure it is correct. Also, check what params are necessary to log in. It may be email, username, etc.
module Sorcery
module TestHelpers
module Rails
def login_user_post(email, password)
page.driver.post(sessions_url, { email:email, password: password })
end
end
end
end
RSpec config:
config.include Sorcery::TestHelpers::Rails
Spec helper:
def app
Capybara.app
end
spec/controllers/protected_resource_spec.rb:
describe UsersController do
before do
# Create user
# Login
response = login_user_post( user.email, :admin_password )
expect( response.headers[ 'location' ]).to eq 'http://test.host/'
# I test for login success here. Failure redirects to /sign_in.
#cookie = response.headers[ 'Set-Cookie' ]
end
specify 'Gets protected resource' do
get protected_resource, {}, { cookie:#cookie }
expect( last_response.status ).to eq 200
end
My application uses Devise for authentication. I want to write integration specs for testing against proper authentication and access prevention.
Somehow, the two don't seem to work together very well. On the devise repo, the README says this on the sign_in and sign_out helpers that Devise gives you for testing:
These helpers are not going to work for integration tests driven by Capybara or Webrat. They are meant to be used with functional tests only. Instead, fill in the form or explicitly set the user in session
So what I'm trying to do to authenticate is filling out the form.
I wrote this (spec/support/signin_helpers.rb):
module SignInHelpers
def sign_in(user)
visit users_login_path
fill_in "Email", with: user.email
fill_in "Passwort", with: "rickroll"
click_button "Einloggen"
end
def login_admin
before(:each) do
sign_in FactoryGirl.create(:admin) # Using factory girl as an example
end
end
def login_user
before(:each) do
user = FactoryGirl.create(:user)
sign_in user
end
end
end
And my tests look like this:
describe "unauthorized access" do
login_user
describe "to Companies#new" do
before { get new_company_path }
specify { response.should redirect_to(root_path) }
end
.
.
.
end
Which seems to work, per se, perfectly fine. No "real" errors thrown. But somehow, somewhere, the authentication gets lost:
5) CompaniesManagement unauthorized access to Companies#index should redirect to "/"
Failure/Error: specify { response.should redirect_to(root_path) }
Expected response to be a redirect to <http://www.example.com/> but was a redirect to <http://www.example.com/users/login>
# ./spec/requests/companies_management_spec.rb:60:in `block (4 levels) in <top (required)>'
What am I doing wrong?
You have put before(:each) in your spec file instead of in support. I mean
describe "unauthorized access" do
before { login_user }
describe "to Companies#new" do
before { get new_company_path }
specify { response.should redirect_to(root_path) }
end
.
.
.
end
In spec/support/signin_helpers.rb you have to write
def login_user
user = FactoryGirl.create(:user)
sign_in user
end
I have the following code in the controller:
# guest to user sign up view. Method that prepares a guest to become a user by emptying it's generic
#e-mail address.
def guest_signup
if !current_user.guest
redirect_to root_url
end
#user = current_user
#user.email = ""
end
This controller just makes sure that the outcome (a form) doesn't have a generic e-mail address in an input field that the user gets assigned when he is using the application as guest.
I am trying to write an rspec test for it and I have no idea how to properly do it... I know this may sound like development-driven testing rather than the opposite but I need an idea.
Currently I have this that doesn't work:
require 'spec_helper'
describe UsersController do
describe "Guest Signup" do
it "should prepare guest with random e-mail user for signup form, emptying the e-mail" do
current_user = User.create(:email => "guest_#{Time.now.to_i}#{rand(99)}#example.com", :password => "#{Time.now.to_i}#{rand(99999999)}", :guest => true)
get :guest_signup, :user => #user.id
expect(#user.email).to eq ('')
end
end
end
How is #user assigned here? Presumably after the guest_signup method is called. Since #user is referenced in the call to guest_signup, you have an order of operations problem here.
Maybe you should be calling:
get :guest_signup, :user => current_user.id
describe UsersController do
describe 'Guest Signup' do
let(:user) { mock.as_null_object }
before(:each) { controller.stub(:current_user) { user } }
context 'when guest does not exist' do
before(:each) { user.stub(:guest) { false } }
it 'redirects to root path' do
get :guest_signup
response.should redirect_to root_path
end
end
context 'when guest exists' do
before(:each) { user.stub(:guest) { true } }
it 'should prepare guest with random e-mail user for signup form, emptying the e-mail' do
get :guest_signup
assigns(#user).should == user
assigns(#email).should be_empty
end
end
end
end
I have devise authentication and registration set up on my Rails app. I'm using after_sign_in_path_for() to customise the redirect when the user signs in based on various scenarios.
What I'm asking is how to test this method? It seems hard to isolate since it is called automatically by Devise when the user signes in. I want to do something like this:
describe ApplicationController do
describe "after_sign_in_path_for" do
before :each do
#user = Factory :user
#listing = Factory :listing
sign_in #user
end
describe "with listing_id on the session" do
before :each do
session[:listing_id] = #listing.id
end
describe "and a user in one team" do
it "should save the listing from the session" do
expect {
ApplicationController.new.after_sign_in_path_for(#user)
}.to change(ListingStore, :count).by(1)
end
it "should return the path to the users team page" do
ApplicationController.new.after_sign_in_path_for(#user).should eq team_path(#user.team)
end
end
end
end
end
but that's obviously not the way to do it because I just get an error:
Failure/Error: ApplicationController.new.after_sign_in_path_for(#user)
RuntimeError:
ActionController::Metal#session delegated to #_request.session, but #_request is nil: #<ApplicationController:0x00000104581c68 #_routes=nil, #_action_has_layout=true, #_view_context_class=nil, #_headers={"Content-Type"=>"text/html"}, #_status=200, #_request=nil, #_response=nil>
So, how can I test this method?
Oddly, I was wondering this very thing today. Here's what I came up with. I created an anonymous subclass of ApplicationController. In this anonymous subclass, I exposed the protected methods that I wanted to test as public methods. Then I tested them directly.
describe ApplicationController do
controller do
def after_sign_in_path_for(resource)
super resource
end
end
before (:each) do
#user = FactoryGirl.create(:user)
end
describe "After sigin-in" do
it "redirects to the /jobs page" do
controller.after_sign_in_path_for(#user).should == jobs_path
end
end
end
On a similar note - if you want to test the redirect after sign-up, you have two options.
First, you can follow a pattern similar to above and very directly test the method in RegistrationsController:
require 'spec_helper'
describe RegistrationsController do
controller(RegistrationsController) do
def after_sign_up_path_for(resource)
super resource
end
end
describe "After sign-up" do
it "redirects to the /organizations/new page" do
#user = FactoryGirl.build(:user)
controller.after_sign_up_path_for(#user).should == new_organization_path
end
end
end
Or, you can take a more integration-testing sort of approach and do the following:
require 'spec_helper'
describe RegistrationsController do
describe "After successfully completing the sign-up form" do
before do
#request.env["devise.mapping"] = Devise.mappings[:user]
end
it "redirects to the new organization page" do
post :create, :user => {"name" => "Test User", "email" => "test#example.com", "password" => "please"}
response.should redirect_to(new_organization_path)
end
end
end
For the newcomers, I would recommend doing this way:
RSpec.describe ApplicationController, type: :controller do
let(:user) { create :user }
describe "After sing-in" do
it "redirects to the /yourpath/ home page" do
expect(subject.after_sign_in_path_for(user)).to eq(yourpath_root_path)
end
end
end
I found this answer through Google recently and thought I would add my solution. I didn't like the accepted answer because it was testing the return value of a method on the application controller vs testing the desired behavior of the app.
I ended up just testing the call to create a new sessions as a request spec.
RSpec.describe "Sessions", type: :request do
it "redirects to the internal home page" do
user = FactoryBot.create(:user, password: 'password 123', password_confirmation: 'password 123')
post user_session_path, params: {user: {email: user.email, password: 'password 123'}}
expect(response).to redirect_to(internal_home_index_path)
end
end
(Rails 5, Devise 4, RSpec 3)
context "without previous page" do
before do
Factory.create(:user, email: "junior#example.com", password: "123456", password_confirmation: "123456")
request.env["devise.mapping"] = Devise.mappings[:user]
post :create, user: { email: "junior#example.com", password: "123456" }
end
end
it { response.should redirect_to(root_path) }
context "with previous page" do
before do
Factory.create(:user, email: "junior#example.com", password: "123456", password_confirmation: "123456")
request.env["devise.mapping"] = Devise.mappings[:user]
request.env['HTTP_REFERER'] = 'http://test.com/restaurants'
post :create, user: { email: "junior#example.com", password: "123456" }
end
it { response.should redirect_to("http://test.com/restaurants") }
end
i have created an rspec test like :
it "should redirect to '/tavern' with an error if user already has a tavern quest" do
user = mock('User')
user.stub(:has_tavern_quest).and_return(true)
post :new_quest, :quest_type => 3
flash[:error].should_not be_nil
response.should redirect_to tavern_path
end
Then, i wrote the controller part :
# check if user already has a tavern quest
if current_user.has_tavern_quest?
flash[:error] = 'You already have a quest to finish !'
redirect_to tavern_path and return
end
And the model part :
def has_tavern_quest?
TavernQuest.exists?(self.id)
end
I would expect that the test succeeds, now but i get :
1) TavernController POST '/quest/' to get a new quest of quest_type == 3 should redirect to '/tavern' with an error if user already has a tavern quest
Failure/Error: flash[:error].should_not be_nil
expected: not nil
got: nil
# ./spec/controllers/tavern_controller_spec.rb:29
Do i have a mistake somewhere ?
THE MACRO FOR LOGIN USER :
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = :user
#user = Factory.create(:user)
sign_in #user
end
end
end
Untested:
it "should redirect to '/tavern' with an error if user already has a tavern quest" do
controller.stub_chain(:current_user,:has_tavern_quest?).and_return(true)
post :new_quest, :quest_type => 3
flash[:error].should_not be_nil
response.should redirect_to tavern_path
end
Your mock doesn't do anything... perhaps you meant to use it somewhere?
I personally dislike mocking in this case and feel it's obfuscation. If you are using Devise you could use their test helpers to sign in as a user.