There was a time when I was all over testability. Looking back I left it at I was going to work on creating some test smells and companion refactorings for a catalog. I have not followed through on that. I ended up spending my time on other things like Continuous Integration and CI Factory: when you are rewarded for a behavior you tend to keep doing it. I stumbled on the mock objects site the other day to find they are compiling a catalog of test smells.
Test Smell: I need to mock an object I can’t replace (without magic)
Test Smell: Everything is mocked
Test Smell: Logging is also a feature
Test Smell: Mocking concrete classes
Test Smell: Bloated Constructor
Test Smell: Confused object
Test Smell: Too many expectations
I especially liked this:
- Keeping knowledge local
- Some of the test smells we’ve identified, such as needing magic to create mocks, are to do with knowledge leaking between components. If I can keep knowledge local to an object, either internal or passed in, then its implementation is independent of its context. I can safely move it wherever I like. Do this consistently and I can build an application as a web of communicating objects.
- If it’s explicit I can name it
- This is why I don’t like mocking concrete classes, I like to have names for the relationships between objects as well the objects themselves. As the legends say, if I have something’s true name then I can control it. If I can see it, I have more chance of finding other uses and so reducing duplication.
- More names mean more domain information
- I find that when we emphasize how objects communicate, rather than what they are, I end up with types and roles defined more in terms of the domain than of the implementation. I think this might be because I have more, smaller abstractions which gets me a further away from the underlying language. Somehow I seem to get more domain vocabulary into the code.
- Pass behavior rather than data
- I find that TDD with mocks encourages me to write objects that tell each other what to do, rather then requesting and manipulating values. Applied consistently, I end up with a coding style where I pass behavior (in listeners and callbacks) from the higher levels of the code to the data, rather than pulling data up through the stack. It’s an unusual style in the Java/C# world, but I find that we get better encapsulation and clearer abstractions because I have to clarify what the pieces will do, not just what values they hold.