Tom -- perfect. That's exactly the kind of explanation I was looking for, and now I can go and deal with these changes with a worry-free mind.

I find it too easy to forget that a class's public methods are, in fact, an API (particularly when there are no libraries involved) and need to be tested as such.

Thanks for taking the time to write this up; I may thumbtack it to the wall for a while.

dwh


Tom Stuart wrote:
On 25 Aug 2009, at 18:12, Denis Haskin wrote:
The key expectation in this example is:
      @discount.should_not be_available(@order)
But I changed the implementation of Discount#available? so that it calls Order#num_products instead of Order#line_items. My examples now fail.
Why am I having trouble getting comfortable with this?

Your specification describes the interaction of Discount with the rest of the system; the idea of mocking is to nail down how a Discount object behaves in isolation from all of the other objects it interacts with. (And then, at a higher level, you have integration tests to check that all of the individual behaviours you've specified play nicely together.) If you change the nature of these interactions, for example by getting Discount to ask Order for a number instead of for a collection of line items, then you're essentially changing the API between different parts of your application so your specs will and should fail.

This is exactly the right amount of brittleness for the level the specs operate at. Your integration tests should still pass, indicating that the system as a whole still works, while your specs fail, indicating that the interactions between objects are not what you specified. Some of the time it does feel like you're fixing up unnecessary breakage but it's important for the specs to be able to reveal failures at this fine-grained level because these sorts of API changes aren't always made deliberately, especially when you're doing big refactorings.

Note that your specs are only "brittle" in the sense that changes in the contracts between collaborating objects ("Discount expects Order to be able to return a collection of line items" changing to "Discount expects Order to be able to say how many products it consists of") require matching changes in those objects' specifications. You can change the implementation of methods on Discount as much as you like -- by replacing poor algorithms with more efficient ones, for example -- and the specs won't necessarily break unless your changes affect the interactions between Discount and "the outside world" of other objects.

Cheers,
-Tom
_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users
_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to