Interfaces and isolation
Roy Osherove has suggested a new name for mocks, fakes, stubs or any test double: Isolation. True, the myriad of test double names can muddy the language, and Meszaros’ suggested name of “test double” still confuses people that don’t get the “stunt double” comparison.
When I first started using mocking frameworks, before I understood the OO design techniques they were intended to support, I used primarily two methods in Rhino Mocks:
- MockRepository.CreateMock
- Expect.Call
Using these two techniques of creating Mock objects and setting expectations, without good OO design, led to a lot of over-specified, brittle tests. Setting expectations that seemed to mirror the system under test seemed to be rampant duplication and hindrances to change. Over time, as I started to learn more about good OO design and the SOLID principles, the issues of brittle and over-specified tests simply went away. Techniques such as:
- Dependency inversion principle
- Interface-based design
- Command-query separation
- Single responsibility principle
- Separation of concerns
All led to better specified behavior in my tests.
Which is why the name “isolation” means nothing to me.
When I’m using interface-based design, I’m doing so not because of some innate desire to increase testability, but because I want to separate concerns and invert my dependencies. I want users of my class to know exactly what is needed for this class to operate. I employ fanatical refactoring to ensure the names and responsibilities of the classes I create are clear to the maintainers of my application.
If I employ the DIP and interface-based design, what exactly am I isolating my class from? Interfaces of which the class already doesn’t care which implementation is provided? Again, I don’t use interfaces solely to swap out a test double in a unit test, but to achieve clear separation of concerns, hone the single responsibility of the class, and invert the dependencies.
When I use Rhino Mocks in the new AAA syntax, I wind up using only three techniques/methods in 99% of cases:
- MockRepository.GenerateMock
to supply my class under test with any dependencies it needs to work - Stub extension method to control indirect inputs
- AssertWasCalled extension method to verify indirect outputs
Following these rules helps me describe very clear behavior in my tests, with obvious results for those reading the tests. I don’t need to isolate when I’m already depending upon abstractions.