When writing code that depends on another class, we sometimes like to stub out the dependency. This can result in test examples that are hard to read and full of details that aren't relevant to the actual test.
it "does something" do
dependency = double(:dependency)
allow(dependency).to receive(:foo).with(:bar).and_return(:baz)
allow(Dependency).to receive(:new).and_return(dependency)
SomeClass.new.some_method
expect(dependency).to have_received(:foo).with(:bar)
end
We could increase the readability of this spec by extracting the setup into a method.
it "does something" do
dependency = stub_dependency
SomeClass.new.some_method
expect(dependency).to have_received(:foo).with(:bar)
end
def stub_dependency
dependency = double(:dependency)
allow(dependency).to receive(:foo).with(:bar).and_return(:baz)
allow(Dependency).to receive(:new).and_return(dependency)
dependency
end
Now the details of the stubbing that are not specific to the test are moved away to somewhere more helpful. On top of this benefit, we can now share the setup between tests.
The next step would be to make the stubbing more readable by using #tap
. #tap
allows us to invoke side-effects on an object before returning the original object.
def stub_dependency
double(:dependency).tap do |dependency|
allow(dependency).to receive(:foo).with(:bar).and_return(:baz)
allow(Dependency).to receive(:new).and_return(dependency)
end
end
Using some well-named methods and #tap
we can make our tests a little bit happier.
It'd be nice if
SomeClass
allowed you to inject the instance ofDependency
instead of knowing how to create it (and forcing you to write test code like this). When I have to useallow to receive :new
I go looking for a way to change how the class I'm testing works