Reputation: 2572
How can I verify if Foobar#some_method
takes a block. Something simliar to Foobar.new.respond_to?(:some_method)
class Foobar
def some_method
yield
end
end
Why?
This is useful for testing contract interface. Ensuring the method I stub has an unchanged API.
Ways I've tried
mth = Foobar.new.method(:some_method)
mth.parameters
this returns a list of parameters (what Rspec uses essentially). It works if I have an argument as block like this:
def some_method(&blk)
end
But if a method uses yield
instead, I got nothing from #parameters
.
This is for adding specs for ensuring the interface with the ugly outside world. So I know how a method is used. But if there is an API change, I'd like specs to fail.
Upvotes: 1
Views: 203
Reputation: 230286
How can I verify if
Foobar#some_method
takes a block.
Every method in ruby takes (can take) a block. It may simply choose not to yield. So that's what you need to check, I think: if the method yielded or not.
RSpec has a number of yield expectations: https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/yield-matchers
RSpec.describe "yield_control matcher" do
specify { expect { |b| MyClass.yield_once_with(1, &b) }.to yield_control }
specify { expect { |b| MyClass.dont_yield(&b) }.not_to yield_control }
specify { expect { |b| MyClass.yield_twice_with(1, &b) }.to yield_control.twice }
specify { expect { |b| MyClass.yield_twice_with(1, &b) }.to yield_control.exactly(2).times }
specify { expect { |b| MyClass.yield_twice_with(1, &b) }.to yield_control.at_least(1) }
specify { expect { |b| MyClass.yield_twice_with(1, &b) }.to yield_control.at_most(3).times }
end
So for your case it should be something like this:
expect{ foobar.some_method }.to yield_control
Upvotes: 3