Using Shoulda to test for an object expecting a method - ruby-on-rails

In the following example the #user.expects(:send_invitations!).once assertion is failing, even though the invitations are being sent by the app and the #send_invitations variable is being assigned. Would you expect #user.send_invitations! to be invoked at this point or is #user.expects(:send_invitations!) being used incorrectly?
The Controller
class RegistrationsController < ApplicationController
before_filter :require_active_user
def welcome
if params[:send_invitations]
current_user.send_invitations!
#send_invitations = true
end
end
end
The Controller Test
require 'test_helper'
class RegistrationsControllerTest < ActionController::TestCase
context "With a logged-in user" do
setup { login_as #user = Factory(:user) }
context ":welcome" do
context "with params[:send_invitations] present" do
setup { get :welcome, { :send_invitations => true } }
should "send invitations" do
#user.expects(:send_invitations!).once # tests say this isn't being invoked
assert assigns(:send_invitations) # but tests say this IS being assigned
end
end
context "without the presence of params[:send_invitations]" do
setup { get :welcome }
should "not send invitations" do
#user.expects(:send_invitations!).never # passes fine
assert !assigns(:send_invitations!) # also passes fine
end
end
end
end
end

Related

Rspec - Stub/allow_any_instance_of included module methods is not working

I've been trying to stub a private module method for the whole day now but with not progress.
Here is a snippet of my application controller class
class ApplicationController < ActionController::Base
include Cesid::Application
end
Cesid > Application.rb
module Cesid
module Application
extend ActiveSupport::Concern
included do
before_action :track_marketing_suite_cesid, only: [:new]
end
private
def track_marketing_suite_cesid
return unless id_token_available?
## #cesid_auth = Auth.new(#id_token)
#cesid_auth = Auth.new(id_token)
return unless #cesid_auth.present? && #cesid_auth.valid?
#cesid_admin = Admin.where(email: #cesid_auth.email).first_or_initialize
end
def id_token_available?
## #id_token.present?
id_token.present?
end
def id_token
#id_token ||= id_token_param
end
def id_token_param
cookies[:id_token]
end
end
end
Now, I'm trying to create a simple unit test for the method
id_token_available?
And I am just trying to set the id_token_param to a random value.
I've tried using this code as stated Is there a way to stub a method of an included module with Rspec?
allow_any_instance_of(Cesid).to receive(:id_token_param).and_return('hello')
but I just get this error
NoMethodError:
undefined method `allow_any_instance_of' for #<RSpec::ExampleGroups::CesidApplication::CesidAuthorizations::GetCesidApplication:0x00007fa3d200c1c0> Did you mean? allow_mass_assignment_of
Rspec file
require 'rails_helper'
describe Cesid::Application, :type => :controller do
describe 'cesid application' do
before do
allow_any_instance_of(ApplicationController).to receive(:id_token_param).and_return('hello')
end
it 'returns true if the id_token is present' do
expect(Cesid::Application.send('id_token_available?')).to eql(true)
end
end
end
Rspec version
3.5.4
This is honestly starting to drive me crazy
I see three issues:
You call allow_any_instance_of in a context in which it is not defined. allow_any_instance_of can be used in before blocks. I need to see your RSpec code to be more specific.
Actually your code is called on the ApplicationController, not on the module, therefore you need to change your stub to
allow_any_instance_of(ApplicationController).to receive(:id_token_param).and_return('hello')
Currently id_token_param will not be called at all, because id_token_available? checks the instance variable and not the return value of the id_token method that calls the id_token_param. Just change the id_token_available? to:
def id_token_available?
id_token.present?
end
There's a much better way of going about this test. The type: :controller metadata on your spec gives you an anonymous controller instance to work with.
Here's an example of how you could write this to actually test that the before_action from your module is used:
describe Cesid::Application, type: :controller do
controller(ApplicationController) do
def new
render plain: 'Hello'
end
end
describe 'cesid before_action' do
before(:each) do
routes.draw { get 'new' => 'anonymous#new' }
cookies[:id_token] = id_token
allow(Auth).to receive(:new).with(id_token)
.and_return(instance_double(Auth, valid?: false))
get :new
end
context 'when id token is available' do
let(:id_token) { 'hello' }
it 'sets #cesid_auth' do
expect(assigns(:cesid_auth)).to be_present
end
end
context 'when id token is unavailable' do
let(:id_token) { '' }
it 'does not set #cesid_auth' do
expect(assigns(:cesid_auth)).to be_nil
end
end
end
end

How to test class method using rspec

Hi i am working on RoR project with ruby-2.5.0 and rails 5.0. I have a model forgot_password where a class method is defined to create a record as follows:-
forgot_password.rb
# frozen_string_literal: true
class ForgotPassword < ApplicationRecord
before_create :create_token
def self.create_record
self.create!(expiry: Time.zone.now +
ENV['VALIDITY_PERIOD'].to_i.hours)
end
private
def create_token
self.token = SecureRandom.urlsafe_base64(nil, false)
end
end
I want to write unit testing for it using stub or factory_girl gem.
spec/models/forgot_password_spec.rb
# frozen_string_literal: true
require 'rails_helper'
describe ForgotPassword do
let(:forgot_password) do
described_class.new()
end
describe 'create_record' do
context 'with forgot_password class' do
subject { forgot_password.create_record.class }
it { is_expected.to eq ForgotPassword }
end
end
end
But its throwing error undefined method create_record for #<ForgotPassword:0x000000000622bc98> Please help me how can i test my model. Thanks in advance.
What you have written is a factory method (a class method that returns an instance) you should call it and write expectations about the instance returned:
describe ForgotPassword do
describe ".create_record" do
subject { described_class.create_record! }
it { is_expected.to be_an_instance_of(described_class) }
it "sets the expiry time to a time in the future" do
expect(subject.expiry > Time.now).to be_truthy
end
end
end
However if what you really are looking to do is set a computed default value then there is a much less clunky way:
class ForgotPassword < ApplicationRecord
after_initialize :set_expiry!
private
def set_expiry!
self.expiry(expiry: Time.zone.now).advance(hours: ENV['VALIDITY_PERIOD'].to_i)
end
end
Or with Rails 5:
class ForgotPassword < ApplicationRecord
attribute :expiry, :datetime,
->{ Time.zone.now.advance(hours: ENV['VALIDITY_PERIOD'].to_i) }
end
You can test it by:
describe ForgotPassword do
let(:forgot_password){ described_class.new }
it "has a default expiry" do
expect(forgot_password.expiry > Time.now).to be_truthy
end
end
You can test against described_class directly:
require 'rails_helper'
describe ForgotPassword do
context 'with forgot_password class' do
subject { described_class }
it { is_expected.to eq ForgotPassword }
end
end

Pundit::NotDefinedError: unable to find policy when moving from Pundit 0.3 to 1.0

When I am running rspec wit pundit version 1.0 on one of my project spec classes I get multiple errors which I haven't seen before. However, when I'm switching to the previous version of pundit (0.3) everything works correctly.
Up to now what I have noticed is that with newer version of pundit #error in create function is not correctly assigned (instead of error class, I get an error message string from the error class).
class ErrorsController < ApplicationController
before_action :set_execution_environment
def authorize!
authorize(#error || #errors)
end
private :authorize!
def create
#error = Error.new(error_params)
authorize!
end
def error_params
params[:error].permit(:message, :submission_id).merge(execution_environment_id: #execution_environment.id)
end
private :error_params
in spec/factories:
FactoryGirl.define do
factory :error, class: Error do
association :execution_environment, factory: :ruby
message "exercise.rb:4:in `<main>': undefined local variable or method `foo' for main:Object (NameError)"
end
end
in spec/controllers/error_controller.rb:
describe 'POST #create' do
context 'with a valid error' do
let(:request) { proc { post :create, execution_environment_id: FactoryGirl.build(:error).execution_environment.id, error: FactoryGirl.attributes_for(:error), format: :json } }
context 'when a hint can be matched' do
let(:hint) { FactoryGirl.build(:ruby_syntax_error).message }
before(:each) do
expect_any_instance_of(Whistleblower).to receive(:generate_hint).and_return(hint)
request.call
end
expect_assigns(execution_environment: :execution_environment)
it 'does not create the error' do
allow_any_instance_of(Whistleblower).to receive(:generate_hint).and_return(hint)
expect { request.call }.not_to change(Error, :count)
end
it 'returns the hint' do
expect(response.body).to eq({hint: hint}.to_json)
end
expect_json
expect_status(200)
end
context 'when no hint can be matched' do
before(:each) do
expect_any_instance_of(Whistleblower).to receive(:generate_hint).and_return(nil)
request.call
end
expect_assigns(execution_environment: :execution_environment)
it 'creates the error' do
allow_any_instance_of(Whistleblower).to receive(:generate_hint)
expect { request.call }.to change(Error, :count).by(1)
end
expect_json
expect_status(201)
end
end
I get the error message
Pundit::NotDefinedError:
unable to find policy Pundit::ErrorPolicy for #<Pundit::Error: {"message"=>"exercise.rb:4:in': undefined
local variable or method foo' for main:Object (NameError)",
"execution_environment_id"=>1}>
since error class is not correctly created. After that every test in error class fail.
My policies:
class AdminOrAuthorPolicy < ApplicationPolicy
[:create?, :index?, :new?].each do |action|
define_method(action) { #user.internal_user? }
end
[:destroy?, :edit?, :show?, :update?].each do |action|
define_method(action) { admin? || author? }
end
end
class ErrorPolicy < AdminOrAuthorPolicy
def author?
#user == #record.execution_environment.author
end
end
I have no such an problem with any other class.
I've been dealing with the same problem for the last half hour, albeit using minitest, and the solution was to run spring stop and then rerun my tests. Hope this helps.

Reset Password testing with Rspec

i am using rails and want to write a test for password reset in Rspec. i am quite new to testing.
this is what i have done so far:
require 'rails_helper'
describe UsersController, type: :controller do
describe 'post #reset_password' do
let(:user) { create(:user) }
context "reset password" do
def do_request
patch :update_password
end
before { do_request }
it { expect(ActionMailer::Base.deliveries.count(1) }
end
end
end
every time i run this it gives ma an syntax error in
"it { expect(ActionMailer::Base.deliveries.count(1) } ".
i want to check whether the email successfully sent of not and if the user have key in the email.
Thanks!
1) you miss ) at last here so got syntax error
it { expect(ActionMailer::Base.deliveries.count(1) }
to
it { expect(ActionMailer::Base.deliveries.count(1)) }
2)
If you want to check total deliveries. you can try
it 'should send an email' do
ActionMailer::Base.deliveries.count.should == 1
end
also check sender
it 'renders the sender email' do
ActionMailer::Base.deliveries.first.from.should == ['notifications#domain.com']
end
Also check subject line
it 'should set the subject to the correct subject' do
ActionMailer::Base.deliveries.first.subject.should == 'Here Is Your Story!'
end
The problems you're having will most likely be fixed by writing better tests.
Here's generally how you would write tests for something like this.
Lets suppose in your routes file you have a post route that looks something like this
# config/routes.rb
post "/user/:id/reset_password", to: "users#reset_password"
And your User controller looks something like this
# app/controllers/users_controller.rb
class UsersController
...
def reset_password
user = User.find(params[:id])
user.reset_password!
SomeMailClass.email_reset_instructions(user)
end
end
and your User.rb model looks something like this
# app/models/user.rb
class User < ActiveRecord::Base
def reset_password!
update!(password: nil) # or whatever way you want/need to reset the password
end
end
and you have some type of mailing class to send your email
# app/models/some_mail_class.rb
class SomeMailClass
def self.email_reset_instructions(user)
# do something to send email...
end
end
The way you would go about testing this in the controller would be
# spec/controllers/users_controller_spec.rb
require 'rails_helper'
describe UsersController, type: :controller do
it "#reset_password" do
user_id = double(:user_id)
user = double(:user)
expect(User).to receive(:find).with(user_id).and_return(user)
expect(user).to receive(:reset_password!).and_return(true)
expect(SomeMailClass).to receive(:email_reset_instructions).with(user)
post :reset_password, id: user_id
end
end
But you shouldn't stop there. Because the implementation of the newly made method reset_password! and the SomeMailClass has yet to be tested. So you would write model/unit tests like this for them
# spec/models/user_spec.rb
require "rails_helper"
describe User do
it ".reset_password!" do
user = User.create(password: "foo")
expect(user.password).to eq "foo"
user.reset_password!
expect(user.password).to eq nil
end
end
Then you might install vcr and factory_girl gems and use them like so to test your mailer
# spec/models/some_mail_class_spec.rb
require "rails_helper"
describe SomeMailClass do
VCR.use_cassette "email_reset_instructions" do |cassette|
it ".email_reset_instructions" do
user = FactoryGirl.create(:user)
SomeMailClass.email_reset_instructions(user)
# you can write some expectations on the cassette obj to test.
# or you can write whatever expectations you need/desire
end
end
end
And in the end if there was something happening on the front end that a user would click that made this post request you would write a feature test for it as well.
Hope this helps!

Controller testing Rspec, Rails 4.2

I am just starting with RSpec and I am a bit lost. Can anyone please tell me how to write controller test for this method?
def list_c
#c = params.has_key?(:car) ? Car.new(car_listing_params) : Car.new()
#car.user = #user
#car.number_of_beds = 4 unless #car.number_of_beds
#car.car_type_tag = 'car' unless #car.car_type
#page_title = t('home.list_your_car')
end
A small example of controller test with rspec. You may get some idea from here.
# define your helper here.
require 'rails_helper'
# respect method to define test for your controller.
RSpec.describe YourController, type: :controller do
#define your factories for the set of your test
# you will decide which one you need to create from factory. Factory girl
# is used to mock factories.
# #c = params.has_key?(:car) ? Car.new(car_listing_params) : Car.new()
# #car.user = #user
# #car.number_of_beds = 4 unless #car.number_of_beds
# #car.car_type_tag = 'car' unless #car.car_type
# #page_title = t('home.list_your_car')
let(:user) { FactoryGirl.create :user }
before { sign_in user }
describe '#title' do
context 'with valid params' do
before do
# in before block, you can request a http call
# with parameter. This action repeats before each test runs.
get :your_action_of_current_controller, {a_param: a_value, a_date: "16-4-2015"}
end
it "renders successfully" do
# when you call a get request it assigns a response object.
expect(response).to be_success
end
it "returns json" do
expect(response.content_type).to be == 'application/json'
end
end
end
end

Resources