Undefined local variable or method 'current_user' (rails) - ruby-on-rails

I am testing my project, it is about application_helper.rb that contain this method :
def delete_link table=""
if current_user.role.name=="Admin" || current_user.role.name=="Pemilik"
link_to 'Hapus', table, :confirm => 'Anda yakin?', :method => :delete
end
end
I want to testing that method, so I make this testing on my customers_controller_test.rb like this
require 'test_helper'
class CustomersControllerTest < ActionController::TestCase
include Devise::TestHelpers
include ApplicationHelper
setup do
#permission = Permission.create(:name=>"Customer")
#role = Role.create(:name=>"Admin")
#user = User.create(:name=>"Admin", :role_id => 1, :email => "email#email.email", :password => "123456")
#role_permission = RolePermission.create(:role_id=>#role.id, :permission_id=>#permission.id, :access=>2)
#customer = FactoryGirl.create(:customer)
sign_in #user
end
test "should link to delete" do
puts assert sign_in #user
current_user = #user
puts current_user.name
puts current_user.role.name
assert delete_link(customer_path(#customer.id))
puts "2"
end
end
But I got Undefined local variable or method 'current_user'. Any idea? Thx for ur advice :)

The current_user method of devise is defined on the application controller and as such is not available in helpers. Supply the user via a parameter of th helper method and you are set.

Related

Wrong values in the parameters passed by rspec

I am trying to test a service but something wrong is happening when I pass the parameters to the service class, the values ​​are being passed in the wrong way by rspec.
My service is:
class CheckInvitesService
def initialize(user, course)
#user = user
#course = course
end
def call
if UserCourseRegistration.exists?(user_id: #user, course_id: #course)
false
else
UserCourseRegistration.create(user_id: #user,
course_id: #course,
school_id: find_school)
end
end
private
def find_school
school = Course.find(#course).school.id
end
end
My test is:
require 'rails_helper'
RSpec.describe CheckInvitesService do
describe "call" do
context 'invite already exists' do
it 'return' do
#current_user_admin = create(:admin)
#school = create(:school, user: #current_user_admin)
#course = create(:course, user: #current_user_admin, school: #school)
# puts #course
# puts #course.id
#verify = CheckInvitesService.new(#course.id, #current_user_admin.id).call
expect(#verify).to be_falsey
end
end
end
end
I printed #course.id and it returns: 122
But when I call the service class, the parameter inside it has another value, for example the #course.id, i passed takes the value: 627
I get the following error:
Failures:
1) CheckInvitesService call invite already exists return
Failure/Error: school = Course.find(#course).school.id
ActiveRecord::RecordNotFound:
Couldn't find Course with 'id'=627
What is entering the class is another id of course the id 627 and not the 122 that should have been passed via parameter.
It appears your arguments are out of order. CheckInvitesService has:
initialize(user, course)
But when you create the CheckInvitesService object, you're passing course as the first argument.
CheckInvitesService.new(#course.id, #current_user_admin.id).call
Should be
CheckInvitesService.new(#current_user_admin.id, #course.id).call
You can use find_or_create_by in your service, it should work, too.
def call
UserCourseRegistration.find_or_create_by(user: #user, course: #course, school: find_school)
end

How to test timestamps using capybara?

I have a program in which a user can post a 'peep', along with it being timestamped. How can I test this using capybara? The problem is everytime a new timestramp is created, the time differs. Would I have to create some sort of stub/mock in capybara? Help would be greatly appreciated. Thank you.
class Peep
include DataMapper::Resource
property :id, Serial
property :title, String
property :text, String
property :created_at, DateTime
end
DataMapper.setup(:default, "postgres://localhost/chitter_#{ENV['RACK_ENV']}")
DataMapper.finalize
DataMapper.auto_upgrade!
App.rb:
require 'data_mapper'
require 'sinatra/base'
require 'sinatra/flash'
require_relative './models/peep.rb'
require_relative './models/user.rb'
ENV["RACK_ENV"] ||= "development"
class Chitter < Sinatra::Base
enable :sessions
register Sinatra::Flash
set :session_secret, 'super secret'
get '/' do
'You arrived at the homepage'
end
get '/peeps' do
#peeps = Peep.all
erb(:index)
end
get '/peeps/new' do
erb(:new)
end
get '/users/new' do
#user = User.new
erb(:'users/new')
end
post '/users' do
#user = User.create(email: params[:email],
password: params[:password],
password_confirmation: params[:password_confirmation])
if #user.save
session[:user_id] = #user.id
redirect to('/peeps')
else
flash.now[:errors] = #user.errors.full_messages
erb(:'users/new')
end
end
post '/peeps' do
peep = Peep.new(title: params[:title], text: params[:text])
peep.save
redirect to('/peeps')
end
helpers do
def current_user
#current_user ||= User.get(session[:user_id])
end
end
run! if app_file == $PROGRAM_NAME
end
Views:
<h1> Peeps </h1>
<ul id='peeps'>
<% #peeps.reverse.each do |peep| %>
<li id="peeps">
Title: <%= peep.title %>
Text: <%= peep.text %>
created_at: <%= peep.created_at %>
</li>
<% end %>
</ul>
If doing this in a capybara driven feature test, the easiest solution is to use one of the time travelling/freezing solutions such as timecop - https://github.com/travisjeffery/timecop - so that your test can be wrapped in a method that freezes the time.
Timecop.freeze(specific_datetime) do
# fill out form
...
# submit form
...
expect(page).to have_content("created_at: #{specific_datetime.tos}") # format the specific_datetime as expected
end
Note: If the date was actually being set in the browser by JS you'd need to also use something like sinon.js to freeze the browser time too.
If you're using RSpec you could probably do something like:
RSpec.describe Peep do
let(:peep) { Peep.create(title: "My title", text: "Some text") }
describe "#created_at" do
it { expect(peep).to respond_to(:created_at) }
it { expect(peep.created_at).to be_kind_of(DateTime) }
end
end
I would recommend not thoroughly testing that the functionality provided by DataMapper works as intended, as doing so would be very involved. It's a third party library that is also tested. Testing that you have added the line property :created_at, DateTime correctly, however, could be achieved with the above specs.

Devise invitation generate accept_invitation_url

I'm using Devise invitable for invitation. Typically, in the invitation email there will be a link to redirect the invitee to the sign_in page, some url like this
mywebsite.com/users/invitation/accept?invitation_token=J-azZ8fKtkuAyp2VZWQX
This url comes from invitation_instructions.html:
<p><%= link_to 'Accept invitation', accept_invitation_url(#resource, :invitation_token => #token) %></p>
Now I want to return the invitation url in my controller as json response, something like this:
def invite
invitee = User.invite!({:email => email}, current_user)
accept_invitation_url = ....
render :json => accept_invitation_url
end
any idea how to get the accept_invitation_url in the controller? Thanks!
try to include the url helpers module in your controller:
class MyController < ApplicationController
include DeviseInvitable::Controllers::UrlHelpers
def invite
invitee = User.invite!({:email => email}, current_user)
render :json => accept_invitation_url(invitee, :invitation_token => invitee.token)
end
end
The URL Helper module for the Devise Invitable Gem can be found here on github
Ok the raw invitation token is not accessible by default because it's a instance variable without accessor (source), there are two ways you could solve this.
The ugly way, without modifying your model class:
def invite
invitee = User.invite!({:email => email}, current_user)
raw_token = invitee.instance_variable_get(:#raw_invitation_token)
render :json => accept_invitation_url(invitee, :invitation_token => raw_token)
end
The clean way, by adding an attribute reader to your user model class:
# User Model
class User < ActiveRecord::Base
attr_reader :raw_invitation_token
# rest of the code
end
# In your controller
def invite
invitee = User.invite!({:email => email}, current_user)
raw_token = invitee.raw_invitation_token
render :json => accept_invitation_url(invitee, :invitation_token => raw_token)
end
Update (16th October 2015):
It seems like the UrlHelper module has been removed and the invitation is handled as a normal route, so you can remove the include DeviseInvitable::Controllers::UrlHelpers and replace the accept_invitation_url call with:
Rails.application.routes.url_helpers.accept_invitation_url(invitee, :invitation_token => raw_token)
I found out that to use accept_invitation_url outside the standard invitation mailer view you need to include inside the mailer the following helper:
include Devise::Controllers::UrlHelpers
I tried Rails.application.routes.url_helpers.accept_invitation_url(invitee, :invitation_token => raw_token) but it is not working.

How to test a controller with steps to use some action

In my system, I have a user that have one company that have multiple accounts.
User sign in system using Devise, and have a virtual attribute called selected_company that was setted in CompaniesController.
I want to make multiple tests in AccountsController with this scenario.
I have this code to sign_in user, this code works well:
before :each do
#user = create(:user)
#user.confirm!
sign_in #user
end
But I must to have a specific context that I tried to code as:
context 'when user already selected a company' do
before :each do
#company = create(:company)
#account = create(:account)
#company.accounts << #account
#user.selected_company = #company
end
it "GET #index must assings #accounts with selected_company.accounts" do
get :index
expect(assigns(accounts)).to match_array [#account]
end
end
But this code won't work, when I run it I got this error:
undefined method `accounts' for nil:NilClass
My AccountsController#index have only this code:
def index
#accounts = current_user.selected_company.accounts
end
I'm new in rspec and TDD and I have some time to test everything I want, and I want to test everything to practice rspec.
I don't know if this is the best way to test this things, so I'm open to suggestions.
Replace with:
expect(assigns(:accounts)).to match_array [#accounts]
Note, :accounts instead of just account.
Also, as I see it, you don't have #accounts in your spec. Please declare that, too. :)
Probably you are not saving selected_company and when you call this on your controller it returns nil.
Try save #user.save after set selected_company:
context 'when user already selected a company' do
before :each do
#company = create(:company)
#account = create(:account)
#company.accounts << #account
#user.selected_company = #company
#user.save
end
it "GET #index must assings #accounts with selected_company.accounts" do
get :index
expect(assigns(accounts)).to match_array [#account]
end
end
Hope to help you.
Finaly, I found the problem!
I changed the before statement to:
before :each do
#company = create(:company)
#account = create(:account)
#company.accounts << #account
controller.current_user.selected_company = #company
end
And changed assigns(accounts) to assings(:accounts) (with symbol) in expect method.

Application variable is nil in testing environment

My application works fine, but I can't get a test to pass. I'm new at rails so forgive me if the answer is obvious.
I need a variable available to every view, so I'm doing this within application_controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :course
def course
#course = Course.find_slug(params[:course])
end
end
My test case looks like this:
it "creates an attempt" do
sign_in current_user
params = {:id => challenge.id, :description => "this was hard!", :course => "design"}
#course = FactoryGirl.create(:course)
post :completed, params
response.should redirect_to "/#{#course.slug}/?challenge_slug=" + challenge.slug
Attempt.count.should == 1
Attempt.last.description.should == params[:description]
end
The method within my controller looks like this:
def completed
#challenge = Challenge.find(params[:id])
#challenge.completed(current_user, params)
redirect_to "/#{#course.slug}/?challenge_slug=" + #challenge.slug.to_s
end
All this works fine if I'm using the application, but the test says:
1) ChallengesController completing a challenge creates an attempt
Failure/Error: post :completed, params
NoMethodError:
undefined method `slug' for nil:NilClass
# ./app/controllers/challenges_controller.rb:16:in `completed'
# ./spec/controllers/challenges_controller_spec.rb:36:in `block (3 levels) in <top (required)>'
If I hardcode my controller to say redirect_to "#{'expected_value'}" then the test passes, so it seems that within the testing environment I don't have access to the application variable #course, is this correct?
I'm lost on how to solve this. Any help is appreciated.
One solution is to stub the find method and return the instance variable.
before(:each) do
#course = FactoryGirl.create(:course)
Course.stub(:find_slug).and_return(#course)
end
This makes your tests more robust as the test for "find_slug" should be in your Course model, not the controller.

Resources