please help display current_user.
i use rails4 + devise + rspec + capybara. i try signin user.
test:
it 'check display username on top panel' do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
sign_in user
visit root_path
binding.pry
end
factories:
FactoryGirl.define do
factory :user do
sequence(:name){ |i| "us#{i}" }
sequence(:email){ |i| "us#{i}#ad.ad" }
password 'qwerty'
password_confirmation{ |u| u.password }
end
end
rails_helper:
..........
..............
RSpec.configure do |config|
...........
..........
config.include Devise::TestHelpers, :type => :controller
end
but I can not display the current user.
console output:
[1] pry(#<RSpec::ExampleGroups::AlbumsController>)> puts user.id
1
=> nil
[2] pry(#<RSpec::ExampleGroups::AlbumsController>)> puts user
#<User:0x000000086cee10>
=> nil
[3] pry(#<RSpec::ExampleGroups::AlbumsController>)> puts current_user
NameError: undefined local variable or method `current_user' for #<RSpec::ExampleGroups::AlbumsController:0x0000000863d348>
from /home/kalinin/.rvm/gems/ruby-2.0.0-p598/gems/rspec-expectations-3.3.0/lib/rspec/matchers.rb:966:in `method_missing'
[4] pry(#<RSpec::ExampleGroups::AlbumsController>)>
If you needs to check login user or not - try to call user_signed_in? helper after sign_in user
sorry for my bad English
You can use subject.current_user to access the current logged in user.
Related
I am using Devise for my user logins and stuff and rspec for testing. I have looked at the Devise testing guide for rspec and mixined ControllerMicros to controller specs.
And actually things are all working fine if I have tests organized like this:
describe 'GET #index' do
context 'user logged in but not admin' do
login_user
it 'should redirect to root_path for non_user' do
get :index
// I have asserted that the current_user here is not nil
expect(response).to redirect_to(root_path)
end
end
end
However, if I have 2 tests in the context and I got current_user is nil for the non-first test.
describe 'GET #index' do
context 'user logged in but not admin' do
login_user
it 'should redirect to root_path for non_user' do
get :index
// I have asserted that the current_user here is not nil
expect(response).to redirect_to(root_path)
end
it 'should do some other thing' do
get :index
// the current_user method returns nil here
expect(response).to redirect_to(root_path)
end
end
end
And the worst part is that it seems this problem is not deterministic: happens somewhat randomly--cause after several failed runs the suite just passed on my computer(but still fails on Travis my build)
Some additional information:
the ControllerMacro.rb
module ControllerMacros
def login_admin
before(:each) do
# #request.env["devise.mapping"] = Devise.mappings[:user]
user = User.find_by(email: 'default_admin#controller.spec')
user ||= FactoryGirl.create(:user, email: 'default_admin#controller.spec', uid: 'default_admin.controller.spec')
admin = Admin.find_by(user_id: user.id)
FactoryGirl.create(:admin, user: user) if not admin
sign_in user
end
end
def login_user(user = nil)
before(:each) do
# #request.env["devise.mapping"] = Devise.mappings[:user]
user ||= User.find_by(email: 'default_user#controller.spec')
user ||= FactoryGirl.create(:user, email: 'default_user#controller.spec', uid: 'default_user.controller.spec')
sign_in user
end
end
end
the rails_helper.rb
RSpec.configure do |config|
# for loading devise in test
config.include Devise::TestHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
end
Your login_user method is run when the test suite load, you should put it in a before :each block to run it once for each test.
describe "GET index" do
before do
login_user
end
it 'blabla' do
get :index
expect(response).to redirect_to(root_path)
end
end
PS : Don't know what you do in your login_user method, but Devise have some nice helpers you can include as follow
#rails_helper.rb
RSpec.configure do |config|
config.include Devise::TestHelpers, type: :controller
end
#then in you test
before do
sign_in user_instance
end
UPDATE from comment
If you have multiple type of user / devise login entry, maybe try to specify the devise mapping you're trying to sign in the user to , as follow :
sign_in :user, user_instance
sign_in :admin, admin_user_instance
please help solve the problem. i use gem 'devise', gem 'rspec' and this tutorial:
Authentication with Devise in Rspec tests
.i need #user output in console.
emails_controller_spec.rb:
RSpec.describe EmailsController, type: :controller do
let(:valid_attributes) {
{
email: Faker::Internet.email,
description: Faker::Lorem.paragraph(7),
user_id: 1
}
}
describe "GET #index" do
login_user
it "assigns all emails as #emails" do
email = Email.create! valid_attributes
get :index
binding.pry
expect(assigns(:emails)).to eq([email])
#expect(assigns(#user)).to eq(#user)
end
end
end
spec/factories/users.rb:
FactoryGirl.define do
factory :user do
sequence(:email){ |i| "us#{i}#ad.ad" }
password 'qwertyui'
password_confirmation{ |u| u.password }
role :none
end
end
spec/support/controller_macros.rb:
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
sign_in user
end
end
end
i user gem 'pry' for see on variables from console after run the test. but i see follow:
kalinin#kalinin ~/rails/mailer $ rspec spec/controllers
From: /home/kalinin/rails/mailer/spec/controllers/emails_controller_spec.rb # line 56 :
[1] pry(#<RSpec::ExampleGroups::EmailsController::GETIndex>)> #user
=> nil
[2] pry(#<RSpec::ExampleGroups::EmailsController::GETIndex>)> user
NameError: undefined local variable or method `user' for #<RSpec::ExampleGroups::EmailsController::GETIndex:0x00000007c4e670>
from /home/kalinin/.rvm/gems/ruby-2.0.0-p598/gems/rspec-expectations-3.3.1/lib/rspec/matchers.rb:966:in `method_missing'
[3] pry(#<RSpec::ExampleGroups::EmailsController::GETIndex>)> puts page.html
=> nil
i need displays user-object or displays html-code of page. please help
I hope you have included your ControllerMacros module somewhere. If not add that to top of your emails_controller_spec.rb file as include ControllerMacros
Add login_user in a before block -
describe "GET #index" do
before { login_user }
it "assigns all emails as #emails" do
email = Email.create! valid_attributes
get :index
binding.pry
expect(assigns(:emails)).to eq([email])
expect(assigns(#user)).to eq(#user)
end
end
Also change your ControllerMacros to:
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
#user = FactoryGirl.create(:user)
sign_in #user
end
end
end
I am testesting my custom devise views with capybara and i need a logged in user in order to tests some views, for example:
feature 'user/edit' do
describe "the edit profile process", :type => :feature do
include Devise::TestHelpers
let!(:user){ FactoryGirl.build(:user)
def sign_in(user)
if user.nil?
request.env['warden'].stub(:authenticate!).
and_throw(:warden, {:scope => :user})
controller.stub :current_user => nil
else
request.env['warden'].stub :authenticate! => user
controller.stub :current_user => user
end
end
it "update " do
sign_in(user)
visit '/users/edit'
within("#edit_user") do
#=> something
end
click_on 'Update'
page.should have_content("something")
end
end
end
But i got this error
Failure/Error: request.env['warden'].stub :authenticate! => user
NoMethodError: undefined method `env' for nil:NilClass
The module Devise::TestHelpers establishes the #request instance variable. You are referencing request, which is apparently set to nil in some other accessible scope, resulting in the error you're getting.
See https://github.com/plataformatec/devise/blob/b8ed2f31608eccb6df6d5bb2e66238d6dfc4bcfc/lib/devise/test_helpers.rb for the source of TestHelpers.
I'm implementing a lazy login feature. My cucumber feature should describe it:
Feature: User log in
Scenario: Lazy login
Given I didn't log out the last time I was on the site
When I go to the homepage
Then I should automatically be logged in
And these are my step definitions:
Given(/^I didn't log out the last time I was on the site$/) do
user = FactoryGirl.create(:user)
visit new_user_session_path
fill_in('user[email]', with: user.email)
fill_in('user[password]', with: 'test123')
click_button('Sign in')
Capybara.reset_sessions!
end
When(/^I go to the homepage$/) do
visit root_path
end
Then(/^I should automatically be logged in$/) do #<-- Fails here
page.should have_content("Logout")
end
This is what happens when a user logs in: the cookies.signed[:auth_token] gets set. This will be used by a before filter in my ApplicationController so that users who open a fresh browser will be logged in automatically:
class SessionsController < Devise::SessionsController
def create
super
if user_signed_in?
puts 'yesssssss'
session[:user_id] = current_user.id
current_user.remember_me! if current_user.remember_token.blank?
cookies.signed[:auth_token] = {
:value => current_user.remember_token,
:domain => "mysite.com",
:secure => !(Rails.env.test? || Rails.env.development?)
}
puts "current_user.remember_token = #{current_user.remember_token}"
puts 'cookies:'
puts cookies.signed[:auth_token]
end
end
end
This is the before filter in my ApplicationController:
def sign_in_through_cookie
logger.info "logging in by cookie"
puts "logging in by cookie"
puts cookies.signed[:auth_token] #<-- PROBLEM: this returns nil.
return true if !current_user.nil?
if !cookies[:auth_token].nil? && cookies[:auth_token] != ''
user = User.find_by_remember_token(cookies.signed[:auth_token])
return false if user.blank?
sign_in(user)
puts 'success'
return true
else
return false
end
end
So the issue is that in the last step of my cucumber feature, cookies.signed[:auth_token] returns nil. I'm guessing this is just a capybara thing. So do I actually have to set a cookie in the test as opposed to using the one in my controller?
So eventually I figured it out after trying a lot of different things.
Given(/^I didn't log out the last time I was on the site$/) do
user = FactoryGirl.create(:user)
visit new_user_session_path
fill_in('user[email]', with: user.email)
fill_in('user[password]', with: 'test123')
click_button('Sign in')
Capybara.current_session.driver.request.cookies.[]('auth_token').should_not be_nil
auth_token_value = Capybara.current_session.driver.request.cookies.[]('auth_token')
Capybara.reset_sessions!
page.driver.browser.set_cookie("auth_token=#{auth_token_value}")
end
When(/^I go to the homepage$/) do
visit root_path
end
Then(/^I should automatically be logged in$/) do
page.should have_content("Logout")
end
UPDATE:
Here's what I use in case I'm using Selenium for some of the tests:
if Capybara.current_session.driver.class == Capybara::Selenium::Driver
auth_token = page.driver.browser.manage.cookie_named('auth_token')[:value]
page.driver.browser.manage.delete_all_cookies
page.driver.browser.manage.add_cookie(:name => "auth_token", :value => auth_token)
else
puts "cookies = #{Capybara.current_session.driver.request.cookies}"
Capybara.current_session.driver.request.cookies.[]('auth_token').should_not be_nil
auth_token_value = Capybara.current_session.driver.request.cookies.[]('auth_token')
Capybara.reset_sessions!
page.driver.browser.set_cookie("auth_token=#{auth_token_value}")
end
Use https://github.com/nruth/show_me_the_cookies which wraps the driver methods. It has methods for getting cookies, deleting cookies, and a method for creating cookies called create_cookie.
I needed just to test the cookie values
Inspiration taken from https://collectiveidea.com/blog/archives/2012/01/05/capybara-cucumber-and-how-the-cookie-crumbles
and ported to Rails 5.x
Create features/support/cookies.rb
With content
module Capybara
class Session
def cookies
#cookies ||= ActionDispatch::Request.new(Rails.application.env_config.deep_dup).cookie_jar
end
end
end
Before do
allow_any_instance_of(ActionDispatch::Request).to receive(:cookie_jar).and_return(page.cookies)
allow_any_instance_of(ActionDispatch::Request).to receive(:cookies).and_return(page.cookies)
end
Then the step for testing
Then('is set cookie {string} with value {string}') do |cookie, value|
expect(page.cookies.signed[cookie]).to eq value
end
I've just started using rails, and decided to follow the "Ruby on Rails Tutorial" by M. Hartl. Seems like a good intro.
Am running into a failed test that's driving me nuts.
I am running rails 3.1.1, with rspec 2.7.0
I have tried modifying the condition, and tests on the "has_password" method work.
The failing test:
1) User password encryption authenticate method should return the user on email/password match
Failure/Error: matching_user.should == #user
expected: #
got: nil (using ==)
# ./spec/models/user_spec.rb:149:in `block (4 levels) in '
The rspec test:
describe User do
before(:each) do
#attr = {:name => 'testing',
:email =>'testing#example.com',
:password => "testtest",
:password_confirmation => "testtest"}
end
...
describe "password encryption" do
before(:each) do
#user = User.create!(#attr)
end
...
describe "authenticate method" do
it "should exist" do
User.should respond_to(:authenticate)
end
it "should return nil on email/password mismatch" do
User.authenticate(#attr[:email], "wrongpass").should be_nil
end
it "should return nil for an email address with no user" do
User.authenticate("bar#foo.com", #attr[:password]).should be_nil
end
it "should return the user on email/password match" do
matching_user = User.authenticate(#attr[:email], #attr[:password])
matching_user.should == #user
end
end
In the User model:
...
def has_password?(submitted_password)
encrypt_password == encrypt(submitted_password)
end
def self.authenticate(email, submitted_password)
user = find_by_email(email) #self.where("email = ?", email)
return nil if user.nil?
return user if user.has_password?(submitted_password)
end
private
def encrypt_password
self.salt = make_salt if new_record?
self.encrypted_password = encrypt(password)
end
I cannot figure out what I'm doing wrong here.
In your failing spec you have
matching_user.should == #user
But #user isn't defined anywhere so it's set to nil.
Edit:
Try adding the following puts into the failing spec and see what results you get in your spec output after running it.
it "should return the user on email/password match" do
matching_user = User.authenticate(#attr[:email], #attr[:password])
puts matching_user # add this
puts #user # and also this
matching_user.should == #user
end