Partly following on from this question. Hopefully the example speaks for itself: there’s a WishlistReporter class which asks one object for data and outputs to another object.
The problem is that with the double for DB, I’m actually testing a whole bunch of things in a single example. Which is not ideal.
I can split the report() method into gather_data() and output() methods. But that doesn’t help: in order to test the output() method I would still need to create the mock db and run gather_data() again.
Is there a way around this?
describe WishlistReporter do
it "should talk to the DB and output a report" do
db = double("database")
db.should_receive(:categories).and_return(["C1"])
db.should_receive(:items).with("C1").and_return(["I1", "I2"])
db.should_receive(:subitems).with("I1").and_return(["S1", "S2"])
db.should_receive(:subitems).with("I2").and_return(["S3", "S4"])
wr = StringIO.new
r = WishlistReporter.new(db)
r.report(db, :text, wr)
wr.seek(0)
wr.read.should =~ /stuff/
end
end
(In reference to the previous question: I’m perfectly happy to mock the Db class because I consider its interface to be external: part of the “what” not the “how”.)
In this sort of situation, I wouldn’t verify the calls to @db because those are read-only calls, so I really don’t care if they happen or not. Yes, of course they do have to happen (otherwise where is the data coming from), but I don’t think of it as an explicit requirement on the behavior of WishlistReporter… If WishlistReporter could produce the report without talking to the database that’d be perfectly fine by me.
I would use
db.stub!instead ofdb.should_receiveand be perfectly happy with that.But for cases where the calls to the object being mocked have side-effects and are explicit requirements, I do something like this. (In this example, for whatever reason we require that the db object be instructed to reload its data before we query it.) Again, the methods that are returning the data don’t need to be explicitly verified, since if the report output is correct, then the data must have been pulled from @db correctly: