Testing current user in helper spec - ruby-on-rails

I have this pretty basic helper which relies on current_user variable provided by Sorcery in controllers and helpers
def current_user_link
user_link current_user
end
def user_link(user, html_options = {}, &block)
link_to user.to_s, user, html_options, &block
end
How can I test this helper?
describe UsersHelper do
describe '#current_user_link' do
it 'should return a link to the current user' do
expected_link = link_to current_user.name, current_user
???
expect(current_user_link).to eq expected_link
end
end
Do I need to stub current_user somehow?
Is it even worth testing?

This is how I solved it.
describe '#current_user_link' do
it 'returns a link to the current user ' do
user = build(:user)
expected_link = link_to user.name, user
allow(controller).to receive(:current_user).and_return(user)
expect(helper.current_user_link).to eq(expected_link)
end
end
PSA: dont forget to call your method on helper.

I was trying to use current_user with Sorcery in an Rspec ApplicationHelper spec and none of the above answers worked for me.
What worked for me was first defining a user with FactoryGirl:
let(:user) { create(:user) }
Then, write an example like this:
it "does stuff" do
allow(helper).to receive(:current_user).and_return(user)
expect(helper.some_method_using_current_user).to do_something
end
Key difference is using the helper object in the example.

simpliest work around is to declare in spec:
let(:current_user) { create(:user) }

you can stub your current_user
describe UsersHelper do
describe '#current_user_link' do
let(:user) { FactoryGirl.build(:user) }
let(:expected_link) { link_to user.name, user }
before { allow_any_instance_of(ApplicationController).to receive(:current_user).and_return(user) }
it { expect(current_user_link).to eq(expected_link) }
end
end
or set your user to session
than you should
let(:user) { FactoryGirl.create(:user) }
and
before { allow_any_instance_of(ActionDispatch::Request).to receive(:session).and_return(user_id: user.id) }

This worked for me:
describe UsersHelper do
describe '#current_user_link' do
it 'should return a link to the current user' do
user = FactoryGirl.create(:user)
allow_any_instance_of(UsersHelper).to receive(:current_user).and_return(user)
expected_link = link_to user.name, user
expect(current_user_link).to eq(expected_link)
end
end
end
In the rails_helper.rb you need to have:
RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, type: :helper
end

When testing helper modules with RSpec, you need to stub the method in your Rspec::ExampleGroups target...
allow_any_instance_of(RSpec::ExampleGroups::UsersHelper).to receive(:current_user).and_return user

For those who came from Devise:
You can simply define the method inside the spec.
describe 'option_for_product_weight' do
before {
def helper.current_user
User.first
end
}
subject { helper.option_for_product_weight }
it 'returns the list' do
expect(subject).not_to be_empty
end
end

Related

Rails Rspec logging in with selected user

I'm facing the problem with logging in rspec with selected user. I've tried making controller module like that:
module ControllerMacros
def login(user)
before(:each) do
#request.env['devise.mapping'] = Devise.mappings[:user]
payload = { jti: SecureRandom.uuid, sub: user.id.to_s }
cookies['access_token'] = JWT.encode(payload, ENV['DEVISE_JWT_SECRET_KEY'], 'HS256')
sign_in user
end
end
end
The issue is that I'm either unbale to pass user in situation like that:
context 'as admin' do
let(:user) { create :user, :super_admin }
login user
before do
get :index
end
it { expect(response).to be_ok }
end
and i get:
Or if I try something like that:
context 'as admin' do
let(:user) { create :user, :super_admin }
before do
login user
get :index
end
it { expect(response).to be_ok }
end
I get:
How can I make it work?
you have created login helper with before(:each) block
In the first case, where you are calling login helper outside the it or before block and passing user instance created using let. here user is not available as scope of let variables is inside the it or before block.
In the second case, where you are calling login helper inside the before block, but login helper also adds before(:each) block. I suspect due to calling of before(:each) within before it raises the error. similar issue reported here
Possible solutions
create user inside the login helper and call it outside the it block
module ControllerMacros
def login
before(:each) do
user = create :user, :super_admin
#request.env['devise.mapping'] = Devise.mappings[:user]
payload = { jti: SecureRandom.uuid, sub: user.id.to_s }
cookies['access_token'] = JWT.encode(payload, ENV['DEVISE_JWT_SECRET_KEY'], 'HS256')
sign_in user
end
end
end
context 'as admin' do
login
before do
get :index
end
it { expect(response).to be_ok }
end
Remove before(:each) block from login helper
module ControllerMacros
def login(user)
#request.env['devise.mapping'] = Devise.mappings[:user]
payload = { jti: SecureRandom.uuid, sub: user.id.to_s }
cookies['access_token'] = JWT.encode(payload, ENV['DEVISE_JWT_SECRET_KEY'], 'HS256')
sign_in user
end
end
context 'as admin' do
let(:user) { create :user, :super_admin }
before do
login user
get :index
end
it { expect(response).to be_ok }
end

RSpec test for ActiveAdmin member_action

I've got custom member_action in my Active Admin panel which is responsible for resending devise reset password instructions.
admin/users.rb
ActiveAdmin.register User do
member_action :reset_password do
user = User.find(params[:id])
user.send_reset_password_instructions
redirect_to(admin_user_path(user),
notice: "Password reset email sent to #{user.email}")
end
end
How to write RSpec tests for such an action? The only thing I found is this one and I think it's not quite related to my problem.
I was trying to sth like below:
require 'rails_helper'
describe Admin::UsersController, type: :controller do
include Devise::TestHelpers
let!(:admin) { create(:admin_user) }
before(:each) do
sign_in admin
end
describe 'GET user' do
let(:user) { create(:user, :random_email) }
before(:each) do
User.should_receive(:find).at_least(:once).and_return(user)
get :show
end
it 'sends email' do
get :reset_password
expect(user).should_receive(:send_reset_password_instructions)
end
end
end
But I'm getting an error:
ActionController::UrlGenerationError:
No route matches {:action=>"reset_password", :controller=>"admin/users"}
Personally I prefer to use a feature test, since when using active admin, UI stuff handle by the framework:
RSpec.feature 'Reset Password', type: :feature do
let(:user) { create :user }
before do
login_as(user, scope: :user)
end
scenario 'can delete future episode' do
visit some_path
click_link 'Reset Password'
expect(page.current_path).to eq(admin_user_path(user))
expect(page).to have_content("Password reset email sent to #{user.email}")
end
end
Ok, it turns out small adjustments (pass the user.id in params) make the trick.
describe Admin::UsersController, type: :controller do
include Devise::Test::ControllerHelpers
before { sign_in admin }
let!(:admin) { create(:admin_user) }
describe 'GET user' do
let(:user) { create(:user, :random_email) }
before do
allow(User).to receive(:find).at_least(:once) { user }
get :show, params: { id: user.id }
end
it 'sends email' do
get :reset_password, params: { id: user.id }
expect(flash[:notice]).to match("Password reset email sent to #{user.email}")
end
end
end

Rails and devise - testing controller when signed in and signed out

So I want to test controller which is using devise.
require 'rails_helper'
describe ArticlesController do
before(:all) { #article = FactoryGirl.create(:article) }
sign_in_admin
describe 'GET new' do
let(:call_request) { get :new }
before { call_request }
context 'admin signed in' do
it { is_expected.to respond_with :ok }
end
context 'admin signed out' do
it { is_expected.to respond_with 302 }
end
end
end
I followed this guide from devise wiki. So I created ControllerMacros module as you can see here:
module ControllerMacros
def sign_in_admin
before(:each) do
#request.env['devise.mapping'] = Devise.mappings[:admin]
#admin = FactoryGirl.create(:admin)
sign_in #admin
end
end
end
And included it, and other needed things to rails_helper:
require_relative 'support/controller_macros.rb'
RSpec.configure do |config|
config.include Devise::TestHelpers, type: :controller
config.extend ControllerMacros, type: :controller
(...)
Here is admin factory:
FactoryGirl.define do
factory :admin do
email 'email#email.com'
password 'password'
end
end
But now I don't know how to sign out admin? As sign_in_admin is executing before each test, but I need to sign admin out for some of the tests. What is the best way to test this controller with signed in/out admin?
btw I'm kinda new to testing
Devise provides some helper methods for testing, including login_user which takes a user, so you shouldn't need to create the sign_in_admin function. Also by using before(:all) at the top of the block, you've signed in the admin for the entire suite of specs.
Use before(:each) within the individual contexts that you want a signed_in_admin.
describe ArticlesController do
describe 'GET new' do
let(:call_request) { get :new }
let(:admin_user) { FactoryGirl.create(:admin) }
before { call_request }
context 'admin signed in' do
sign_in(admin_user)
it { is_expected.to respond_with :ok }
end
context 'admin not signed in' do
it { is_expected.to respond_with 302 }
end
end
end
Better way is to close before action into a context. The contexts are signed_in, and non-singed in. So you can do something as follows:
describe 'GET new' do
let(:call_request) { get :new }
before { call_request }
context "signed in" do
before { sign_in_admin }
it { is_expected.to respond_with :ok }
end
context 'non-singed in' do
it { is_expected.to respond_with 302 }
end
end

Draper Decorator spec not able to use Devise current_user method

I have a PostDecorator class in app/decorators/post_decorator.rb.
It has a method that calls Devise's current_user method. It looks like this:
class PostDecorator < Draper::Decorator
delegate_all
def voter
h.current_user
end
end
I have a PostDecorator spec in spec/decorators/post_decorator_spec.rb
require 'spec_helper'
describe PostDecorator, type: :decorator do
let(:post) { FactoryGirl.create(:post) }
let(:user) { FactoryGirl.create(:user) }
before { allow(helper).to receive(:current_user) { user } }
describe 'voter' do
it 'returns the current_user' do
expect(post.voter).to eq user
end
end
end
When I run this I get an undefined method error:
<Draper::HelperProxy:0x007fb1c4f85890 ... does not implement: current_user
Gem Versions:
draper (1.4.0)
rspec-rails (3.4.1)
devise (3.5.5)
Also I should note everything in my app/lib directory is auto loaded. In application.rb I have config.autoload_paths << Rails.root.join('lib')
Two things I think you need to do.
1.) Add devise test helpers to your decorator tests,
RSpec.configure do |config|
config.include Devise::TestHelpers, type: :decorator
end
2.) you actually need to sign_in to expect post.voter to eq an actual users
require 'spec_helper'
describe PostDecorator, type: :decorator do
let(:post) { FactoryGirl.create(:post) }
let(:user) { FactoryGirl.create(:user) }
before do
sign_in user
end
describe '.voter' do
it 'returns the current_user' do
expect(post.voter).to eq user
end
end
end
The issue is related to Draper.
Decorator is unable to access helper methods after sending an ActionMailer email.
This is an open issue on Draper's GitHub
To solve this I just modified the User Factory by adding a confirmed_at:
factory :user do
...
confirmed_at DateTime.now
end
This way Devise won't send a Confirmation email.

How to access (devise) current_user in a rspec feature test?

In the devise documentation they give tips on how you can have access to current_user when testing a controller:
https://github.com/plataformatec/devise/wiki/How-To:-Test-controllers-with-Rails-3-and-4-%28and-RSpec%29
However, what about when doing a feature test? I am trying to test a create method of one of my controllers, and in that controller is used the current_user variable.
The problem is that the macro suggested in devise uses the #request variable, and it is nil for a feature spec. What is a workaround?
EDIT:
This is what I have so far for my current spec:
feature 'As a user I manage the orders of the system' do
scenario 'User is logged in ad an admin' do
user = create(:user)
order = create(:order, user: user)
visit orders_path
#Expectations
end
end
The problem is that in my OrdersController I have a current_user.orders call, and since current_user is not defined, it will redirect me to /users/sign_in.
I have defined this under /spec/features/manage_orders.rb
from https://github.com/plataformatec/devise/wiki/How-To:-Test-controllers-with-Rails-3-and-4-%28and-RSpec%29
if i have understood you right, maybe you need to use
subject.current_user.email
#or
controller.current_user.email
for example :
describe OrdersController, :type => :controller do
login_user
describe "POST 'create'" do
it "with valid parametres" do
post 'create', title: 'example order', email: subject.current_user.email
end
end
end
controller_macros.rb :
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
#user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the "confirmable" module
sign_in user
end
end
end
Don't forget to include this into your spec_helper.rb :
config.include Devise::TestHelpers, type: :controller
config.extend ControllerMacros, type: :controller
Here's what I think you are looking for:
require 'spec_helper'
include Warden::Test::Helpers
Warden.test_mode!
feature 'As a user I manage the orders of the system' do
scenario 'User is logged in ad an admin' do
user = create(:user)
login_as(user, scope: :user)
order = create(:order, user: user)
visit orders_path
#Expectations
end
end
you can define login_user as a method for the user to login as follows (put it in support folder):
def login_user
Warden.test_mode!
user = create(:user)
login_as user, :scope => :user
user.confirmed_at = Time.now
user.confirm!
user.save
user
end
Then in the scenario say:
user = login_user

Resources