As I am getting older my memory doesn't work as it did some years ago. If want to remember and internalize the important stuff from all the things I study, I just have to blog about them for two reasons:
- being able to come back to my blog to reread
- record it better on my mind - forcing my brain to explain what I learn produces a deeper learning experience
I was lucky to participate in a nightly mob programming session with Sandro Mancuso and other fellow craftsmen during Socrates Canaries when I first saw his way of writing acceptance tests. Now with Sandro's Screencasts we have a great material to study and explore. We have to thank Sandro for exposing himself with these code screencasts specially because they are for free and unedited. What happens with screencasts (because I record screencasts myself) is that after a while, perhaps after a bunch of months you change your way of coding, you find little tweaks along the long journey of continuous improvement that may make you feel a bit embarrassed when you review your old videos. Specially for this I appreciate the effort Sandro is putting into the community.
The main pick I take from the recorded kata is what I would call an "evolving acceptance test" and "deferred dependency injection". When I write a test, I don't want to change it later. In TDD the production code evolves with each test however my tests should not change even when I refactor the code. And this is also true for the acceptance tests when it comes to the "act" and "assert" parts but not necessarily to the "arrange". I have learned that I can postpone the decision of where and how are my dependencies going to be connected. On Outside-in TDD #1, Sandro knows there is a Console object and spies on it but the object is not injected yet into the Account object - by the way, the kata is better understood if the Account was was named AccountService. He defers the decision on how to wire up dependencies. To make it more intention revealing what I am doing now is to get the service under test from a factory:
- AccountService service = Factory.AccountService();
Now all I have to do is to change the factory method whenever I am done with the low level unit tests. But I don't have to have the object graph on my head at the time I am writing that very first test. Nice one.
Now when it comes to the unit tests, my outside-in style used to be very similar to Sandro's but in the last couple of years it has changed slightly. I try to mock only the boundaries of the system. Only those things I don't own. As an example, on the Outside-in TDD #2, at about 2mins:49secons he creates a test to spy on the StatementPrinter. That test turns out to be exactly like the production code which makes it pass. It is strongly coupled to the implementation. I would rather use a Console spy to test that properly formatted statements are sent to the console, thus hiding the intermediate collaboration step from my test.
But I would do so probably using Sandro's technique of deferred dependency injection - I don't know whether I really want to inject the Console into the Account. So the production code will likely employ the StatementPrinter but my test wouldn't know about it. To shorten the feedback loop I often fake the implementation (yes, "return true"), that gets me an idea of the complexity of the problem at hand. Then I turn to read and triangulate to get to green with the minimum effort. Eventually I would end up with the StamentPrinter as I refactor.
Essentially I like my tests to know only what is strictly needed from the implementation so that I can refactor often without breaking many tests.
What about you? What things do you do differently?