allow_any_instance_of to count up number of method calls behind the scene
2021-08-25
RubyRspec is one of the most popular test framework in Ruby, and of course it offers lots of functionalities for being able to write various type of test cases, like mock, stub, matcher, etc.
allow_any_instance_of
is a powerful method, which is able to stub any instance of a class even though using it is not encouraged.
https://github.com/rspec/rspec-mocks#settings-mocks-or-stubs-on-any-instance-of-a-class
Problem
The problem I want to solve is counting up the number of calls of a method.
I thought it can be done easily with .exactly(N).times
matcher, but it can't.
When you have Hoge
class with hoge
method.
class Hoge
def hoge
end
end
Then, write a small spec.
it 'fails with exactly matcher' do
expect_any_instance_of(Hoge).to receive(:hoge).exactly(3).times
Hoge.new.hoge
Hoge.new.hoge
Hoge.new.hoge
end
This spec fails due to the following error.
Failure/Error: Hoge.new.hoge
The message 'hoge' was received by #<Hoge:1640 > but has already been received by #<Hoge:0x00007fce52827968>
The reason of this failure is that expect_any_instance_of
is to set an expectation on an instance.
It means it can't set expectations across more than one instances.
How to solve
To avoid the error, give a proc to count up method calls locally.
it 'succeeds with giving proc to count up' do
n = 0
allow_any_instance_of(Hoge).to receive(:hoge) { n += 1 }
Hoge.new.hoge
Hoge.new.hoge
Hoge.new.hoge
expect(n).to eq(3)
end
This should succeed.
In addition, you might think why I didn't use let
to declare a variable within a spec, however, let
doesn't work expectedly in this case because let
actually declare not a variable but a function.
Thus, the following snippet returns undefined method '+' for nil:NilClass
error since it attempts to use n
as a variable which is defined but not a variable
let(:n) { 0 }
it 'succeeds with giving proc to count up' do
allow_any_instance_of(Hoge).to receive(:hoge) { n += 1 }
Hoge.new.hoge
Hoge.new.hoge
Hoge.new.hoge
expect(count).to eq(3)
end
You can see the entire source code at Gist: https://gist.github.com/petitviolet/9953af0aa561ea49c5e2aa13dd52f2ef