RSpec - Check whether method was called - ruby-on-rails

My goal is to write rspec test that check whether method was called.
notify(result) if notification_allowed?(result)
Both notification_allowed? and notify are private methods. More precisely I need a test that check whether notify method has was called. I've tried to do something like below but it doesn't seem to be right.
subject { described_class.new }
it do
expect(subject).to receive(:notify)
subject.send(:notification_allowed?, true)
end

Calling notification_allowed? doesn't do anything except return the result of notification_allowed? It's not related to notify
You would need to call the function that contains the expression you want to test.
For example, the method might be...
def check_and_notify(result)
notify(result) if notification_allowed?(result)
end
so the test would be...
subject { described_class.new }
it 'calls notify' do
expect(subject).to receive(:notify)
subject.send(:check_and_notify, true)
end

Related

how to check if a method is called or not in rspec

as i try to check internally my method was calling or not in rspec but it got the following errors
context "#Meeting_schedule" do
let(:meeting_schedule) { FactoryGirl.create(:meeting_schedule,:time=>"morning",:schedule_name=>"planned_meet", :schedule_info=>[{ "from"=>"00:00", "to"=>"00:01"}]) }
it "if the same schedule was created again dont save it again" do
schedule.save
params = {:time=>"morning",:schedule_name=>"planned_meet", :schedule_info=>[{ "from"=>"00:00", "to"=>"00:01"}]}
meeting_schedule.create_or_update_meeting_schedule(params)
expect(meeting_schedule).to receive(:updating_the_user)
end
end
i got the following error
Failure/Error: expect(meeting_schedule.create_or_update_meeting_schedule(params)).to receive(:updating_the_user)
(#<Meeting_schedule:0x0055dbaf0da710>).updating_the_user(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
# ./spec/models/meeting_schedule_spec.rb:122:in `block (4 levels)
so what was wrong in my code?
my method
def create_or_update_meeting_schedule(params)
self.attributes = params
if self.changed and self.save
updating_the_user
end
self
end
can anyone help me out
Mocks must always be setup before the method under test is called as there is no reliable way to test if a normal method was called in Ruby. There are two ways of doing this in RSpec.
The first is using expect(...).to receive(...) which must be done before the method is called - this detaches the method and replaces it with a mock that wraps the original method.
The test will fail if the method is not called in the example.
The second is by using spies. You can either replace an entire object with a spy:
RSpec.describe "have_received" do
it "passes when the message has been received" do
invitation = spy('invitation')
invitation.deliver
expect(invitation).to have_received(:deliver)
end
end
This "spy object" will keep track of any method you call on it.
You can also spy on a single method:
class Invitation
def self.deliver; end
end
RSpec.describe "have_received" do
it "passes when the expectation is met" do
allow(Invitation).to receive(:deliver)
Invitation.deliver
expect(Invitation).to have_received(:deliver)
end
end
Spies are very useful in the case when you want to mock the method in the test setup - for example in the before block.
class Invitation
def self.deliver; end
end
RSpec.describe "have_received" do
before do
allow(Invitation).to receive(:deliver)
end
it "passes when the expectation is met" do
Invitation.deliver
expect(Invitation).to have_received(:deliver)
end
end

How to stub zendesk_api current_user for a spec?

I have a model method which I am trying to write a spec for. The method is like this:
def my_method
puts current_user.user_attirbute
end
Where current_user is provided by an authentication gem, zendesk_api-1.14.4. To make this method testable, I changed it to this:
def my_method(user_attribute = nil)
if user_attribute = nil
user_attribute = current_user.user_attribute
end
puts user_attribute
end
This refactor works and is testable, but doesn't seem like a good practice. Ideally the gem would provide some sort of test helper to help stub/mock the current_user, but I haven't been able to find anything. Any suggestions?
You can go simple way and just test returning of proper value by current_user#user_attribute method. Example:
describe '#my_method' do
subject { instance.my_method } # instance is an instance of your class where #my_method is defined
let(:user) { instance_spy(ZendeskAPI::User, user_attribute: attr) }
let(:attr} { 'some-value' }
before do
allow(instance).to receive(:current_user).and_return(user)
end
it { is_expected.to eq(attr) }
end
But I would go with VCR cassette(vcr gem is here) because it is related 3rd party API response - to minimize a risk of false positive result. Next example demonstrates testing with recorded response(only in case if #current_user method performs a request to zendesk):
describe '#my_method', vcr: { cassette_name: 'zendesk_current_user' } do
subject { instance.my_method }
it { is_expected.to eq(user_attribute_value) } # You can find the value of user_attribute_value in recorded cassette
end
P.S. I assumed that you put puts in your method for debugging. If it is intentional and it is part of the logic - replace eq with output in my example.

Rspec: How to expect received message without stubbing anything or changing anything behind the scenes

I have this Rspec test for a rails app
describe '.move_probes_to_master_list' do
let(:as) { create_list :a, 3, }
let(:bs) { create_list :b, 3 }
it 'sums the volumes' do
active_as= [as[1], as[2]]
b_ids = [2, 3]
expect_any_instance_of(A)
.to receive(:required_volume).twice.with(b_ids)
expect_any_instance_of(A)
.to receive(:update_move_to_master_list).twice
expect(A.calculate_volume(active_as)).to eq(true)
end
end
Basically I call A.calculate_volume and inside this class method, I want to ensure that some member of the A class is recieving some other messages as well. I don't want to stub out those methods, I want them to run as normal, but I just want to verify that the methods are being called.
This is being run in a loop, so I don't know exactly what instances I'll be dealling with, but I want to make sure that both messages are called on some members (but not necessarily the same member both times) of the A class twice in total.
If I remove the expect_any_instance_of(A).to receive expectations everything runs fine and the test passes.
If I keep them, the method call fails and the test breaks.
I tried adding and_call_original but I feel like I'm shooting in the dark because the docs aren't clear on how these methods actually operate.
So how can I verify that an instance of some class recieves a message n times without changing anything else about the method call?
Am I missing the point of expect to receive here? It's not obvious to me why it would stub anything in the first place.
You could spy on the method like this:
allow(A).to receive(:calculate_volume).and_call_original
Then you could test that calculate_volume has been called like this:
expect(A).to have_received(:calculate_volume)
This way the original method will be called and there would be no stubbing but spying.
I know this doesn't exactly answer your question "how to continue execution", but you should be able to break this test into three distinct tests and create a more clear set of tests while not having to worry about calling the original as such:
describe '.move_probes_to_master_list' do
let(:as) { create_list :a, 3, }
let(:active_as) { [as[1], as[2]] }
let(:bs) { create_list :b, 3 }
let(:b_ids) { [2, 3] }
subject { A.calculate_volume(active_as) }
it 'sums the volumes' do
expect(subject).to eq(true)
end
it 'calls #required_volumen twice' do
expect_any_instance_of(A)
.to receive(:required_volume).twice.with(b_ids)
subject
end
it 'calls updates_moves_to_master_list twice' do
expect_any_instance_of(A)
.to receive(:update_move_to_master_list).twice
subject
end
end

How do you cause an expectation to halt execution in RSpec?

I want to test that a class receives a class-method call in RSpec:
describe MyObject do
it "should create a new user" do
expect(User).to receive(:new)
MyObject.new.doit
end
end
class MyObject
def doit
u = User.new
u.save
end
end
The problem is that the expectation does not halt execution. It simply stubs the class method .doit and continues execution.
The effect of the expectation is to ensure that User.new returns nil. So when we get to the next line which is User.save it then fails because there is no user object to call .save on.
I would like execution to halt as soon as the RSpec expectation has been satisfied - how can I do that?
nb
This is just an illustrative example - while an expect to change would work for User.new, it's not this actual code that I need to test
There is a great method for this and_call_original:
expect(User).to receive(:new).and_call_original
based on your test description, you're testing that a record was created, in those cases I would suggest you to do this:
expect {
MyObject.new.doit
}.to change{User.count}
or if you want to make sure it only created one:
expect {
MyObject.new.doit
}.to change{User.count}.by(1)

RSpec: expect.to receive fails if object is not referenced directly

In my Rails application I have a User model:
class User
def self.foo
User.all.each{ |user| user.bar }
end
def bar
end
end
In my spec file I want to check that foo calls bar for every user, so far that's what I have:
describe '::foo' do
let!(:users) { Fabricate.times(5, :user) }
it 'calls bar for every user' do
users.each do |user|
expect(user).to receive(:bar)
end
User.foo
end
end
Although the method gets called (I debugged it, so I'm sure of that) the spec is red.
Also I tried to write this code to understand where the problem was:
let!(:user) { Fabricate(:user) }
it 'fails' do
expect(user).to receive(:bar)
User.first.bar
end
it 'pass' do
expect(user).to receive(:bar)
user.bar
end
It seems that if I reference my instance directly it works, if I obtain it from the DB the expectation doesn't work.
I use mongoid, not sure if this is relevant.
I believe it cannot be done due to how RSpec works: When you set an expectation, RSpec essentially 'wraps' the object so that it can keep track of the messages it receives.
But when the implementation code fetches records from the database, they are not wrapped, so RSpec isn't able to record their messages.
RSpec does have a method allow_any_instance_of which can help in some cases, but its use is discouraged, and don't think it would be suitable here.
In this situation, I would suggest stubbing User.all to return some doubles (two should be sufficient). You can then verify that bar is called on each one.

Resources