Lesson 1 – “Specify conversations between objects an abstract enough level”
A common complaint with using mock objects is that interaction based tests ties your tests to the implementation details of the production code and interaction based tests are tracking the implementation of the method you’re testing. I felt this many times previously, every time I refactored my production code some of my tests seem to break even though, functionality of the object remained the same. Often mocks and interaction based testing are blamed for brittle tests. I have come to realize any brittleness exposed by interaction based tests is just a symptom of an over specified test or their is an underlying design issue because of a weak interaction model.
Interaction tests will be brittle and tied to the implementation if you dont specify interactions at the right level, and your tests will quickly let you know. After spending time doing some development in the Smalltalk world, i rediscovered that OO is all about message passing (object interactions). So a good OO model must have a good interaction model, with this in mind i have come to realize that the key to interaction based testing is specifying interactions (or conversations) between an object and its collaborators at the right level. An object should tell its collaborator what it should get done and not how it should do it. This drives us not to include even a hint of implementation detail when designing our interactions between the object under test and it collaborator, this forces us to create collaborator interface at an abstract enough so that no implementation details gets in the way, this leads to what is often called interface discovery of the collaborating objects, and is a great exploratory way to design the interface of collaborators and distribute responsibilities between objects and mocks are a vital tool to do this. This is what Steve Freeman and Nat Pryce have been telling us all along, but its only now I’m starting to understand this. However i also learnt not to use mocks everywhere, generally if I cannot model interactions between an object and its collaborator at an abstract enough level that made sense to the calling objects domain, it generally means i’m unable to define a clear role interface for the collaborating object, then in this case i would not use mocks at all, rather I tend to use a real object or a stub and test a cluster of objects together.
So what do I mean by “Specifying conversations between objects an abstract level”. I will try explaining by giving an example in the next post