I’ve always used the idiom of stub collaborations in a before block
and then focus in specific examples with should_receive. (e.g.
should_receive takes presence over stub). I was just attempting to
write an example for caching behavior and ran into something counter-
intuitive, at least IMHO. See the following(contrived) example.
class ExpensiveOperation
def self.lookup(question)
“This goes to the database”
end
end
class UsesExpensiveOperation
def initialize
@cache = {}
end
def answer_for(question)
# oops, this is broken, it was supposed to be cached
# @cache[question] ||= ExpensiveOperation.lookup(question)
@cache[question] = ExpensiveOperation.lookup(question)
end
end
describe “suprising behavior” do
before do
@object = UsesExpensiveOperation.new
ExpensiveOperation.stub(:lookup).and_return('whatever')
end
it "should fail because I'm specifying that lookup should be
called at most one time, but that’s not true" do
ExpensiveOperation
.should_receive(:lookup).at_most(:once).and_return(‘an answer’)
@object.answer_for("my question")
@object.answer_for("my question")
end
end
This seems dangerous to me. Assuming I hadn’t initially stubbed in the
before block and everything worked as expected, if someone later
stubs :lookup in the before block because they are adding new examples
that don’t care about it, my explicit example becomes misleadingly
useless.
Does this surprise anyone else?
-lenny