Reputation: 1123
I'm learning rspec, and I'm wondering what the most effective way to write specs for a method that calls a chain of other methods. For example:
class Example1
def foo(dependency)
dependency.bar("A")
dependency.baz("B")
dependency.bzz("C")
end
end
Ideally I would like to write specs like this:
it "should call bar" do
ex = Example1.new
dep = mock
dep.should_receive(:bar).with("A")
ex.foo(dep)
end
it "should call baz"
...
it "should call bzz"
...
When I do that, however, I (understandably) get exceptions like 'unexpected method call baz'.
So what's the best way to deal with that? I have come up with a couple of ideas but I don't know if any of them are good.
Upvotes: 0
Views: 918
Reputation: 15736
It sounds like you have already worked out which options RSpec gives you. I would go with option 1 and use as_null_object
. It's true that you might be missing other random method calls on that object but I would be ok with that if the point of each of these tests was simply to assert that a particular method was being called, especially if I have higher level integration tests covering this method.
If you really need to verify that no other methods are called on dependency
then option 3 may make sense but such tests can be brittle when implementation changes.
As an aside, to make your test a little simpler you can use subject
to avoid explicitly instantiating Example1
(assuming you are using a describe Example1
block), e.g.:
subject.foo(dep)
(However see discussion in comments - an implicit subject can hide intention).
Upvotes: 1
Reputation: 21791
I would test this code in this way:
describe "checking foo method" do
before(:each) do
@ex = Example1.new
@test = ClassOfDependency.any_instance
@test.as_null_object
end
after(:each) do
@ex.foo(dependency)
end
it "should call bar method" do
@test.should_receive(:bar).with("A")
end
it "should call baz method" do
@test.should_receive(:baz).with("B")
end
it "should call bzz method" do
@test.should_receive(:bzz).with("C")
end
end
But I'm not sure that it will work, hope it'll give you some ideas.
Upvotes: 1
Reputation: 11076
RSpec has a feature called stub_chain: https://www.relishapp.com/rspec/rspec-mocks/v/2-0/docs/stubs/stub-a-chain-of-methods
What about testing them all in one example?
it "should call bar"
ex = Example1.new
dep = mock
dep.should_receive("bar").with("A")
dep.should_receive("baz").with("B")
dep.should_receive("bzz").with("C")
ex.foo(dep)
end
I believe you can use RSpec to verify the order in which they are called, if that matters.
However, this kind of approach often indicate that there is a problem with how the code is written, e.g. a Law Of Demeter violation. In your example, foo
should be a methed on the dependency's class.
Upvotes: 1