*
When I do Test Driven Development and I have the choice of doing it how I like then I start from the Top-Down. I choose Top-Down because it keeps me focused on the goal and what the Business/User wants. With Top-Down I go down fewer rabbit holes looking for an elusive bug or wrestling with a tricky implementation.
I start by writing down what it is I am building and what tests must pass before I accept the work as done. For example, I am working on a Smalltalk compiler for the Java Virtual Machine and one of the deliverables is the compiler, so this is how I started the acceptance tests:
The compiler will be done when …
- Compiling Statements made up of a single Expression
- Compiling Methods with a Message Pattern followed by Statements
I try to write all the criteria down before starting any implementation. I do this so I can prioritize and see the scope of what I am doing. I then convert these sentences into the syntax of the language I am developing in, sometimes this makes the test method names more uniform but I try not to loose important information:
compilingStatementsWithAnExpression()
compilingMethodsWithMessagePatternAndTemporariesAndStatements()
The next and probably most important step is writing the line of code that will prove or disprove that the criteria has been met. I write this before any implementation. If I can’t form a hypothesis in code before writing implementation code then I’m usually not sure of what I am doing and I should step back and become sure with a spike. I follow the same process of writing the verification or assert statement in English and converting it to the target language. Here are the verification steps for the above tests, assuming the use of the Java Mockito Testing framework (in my case the compiler is done when it can call the code generator with a valid abstract syntax tree):
verify(generate).receive(statementWith(expression()))
verify(generate).receive(methodWith(messagePattern(), temporaries(), statements()))
I use the features of the language or the testing framework to facilitate code that is descriptive, even if this means a little more work since the test also serves as documentation. In my case I can tick off each test against the Smalltalk grammar shown in the back of the book Smalltalk-80: The Language and its Implementation.
When I have all the tests ready I sometimes comment out all but the one I want to implement first and make a start on implementing it. Commenting out the others means I won’t get swamped and disheartened by a flood of red-bars (failing tests). As I implement each test I leave it uncommented and move onto the next test. Eventually all the code is uncommented and passing (green-bar).
The test I implement first is driven by the Business as it is typically the feature they want first or the one that shows them or me the most important information to make decisions on moving forward.
When implementing I fake-it-until-i-make it, that is, I hard code the results output from the code under test to ensure the test passes, then I go back an remove the hard coding and implement the real logic. I do this because it provides an opportunity to see more of the requirements to get the test to pass and an opportunity to see the test is correct. There have been a few times when the test was incorrect and passing and therefore the code was not correct and I was none the wiser. Not happy times. While using fakes I output a message to let me know a fake is in use, so I remember to remove them all.
The above approach is the one that works best for me and I suggest you try it as I find it yields less code overall.
ADDENDUM:
I received some feedback that suggested I may not be following the failing-test-implement-pass-refactor’ cycle and the approach I use does, as I start with a test that fails because I have not yet faked it. I then implement the fake and the test passes. I then continue to fake more tests to show two important things, 1. The tests work and, 2. The commonality in tests and the potential implementation. At some point in the process I will have no fake code at all and the actual implementation will have been done after a failing test. I have seen the false-positives scenario too many times so I stop and test the test just a little more. YMMV
* The image used here is by Cannboys and you can see his portfolio here: http://www.istockphoto.com/cannboys