One of the selling points of TDD is the ability to shape an interface of a class before actually implementing it. It’s when coding a test, you use classes and methods that don’t exist yet, ignoring the IDE telling you off for writing not-compilable code. Yeah, that’s really cool, I never do that… Well… that’s not entirely true: I rarely do that in tests. Let’s not forget that outside of the test code there is a system with loads of collaborating classes. The best place to design new code is there – not in the test but somewhere in the maze of classes. I would place new instances, new method calls there and when I’m happy with the design I would quick-fix compilation errors by creating empty classes/methods. Now it’s finally time for writing test code! Things to bear in mind to avoid the always-start-in-test gotcha and write better code:
- Test Driven Design not always means to start designing in the test
- Test-First not always means to start writing any code in the test
I had a shiny new story to implement: the user wanted to type url and see tabular report of cucumber sale for the past year. Off we started: My pair created a new class CucumberDatabaseTest.
Bang! I hit him in the hand. I hit him hard.
I violently tore the keyboard out of his hands and started ‘writing’ different test: I typed ‘http://localhost/cucumberYearlyReport’ in the browser and told him:
“this is my test. Why doesn’t it work? Why do I see 404? I want to see cucumbers!”
So then my pair started fixing my broken test. He created mapping for url and a controller. But we didn’t know what should be the exact url. So we asked the business. The business said:
“Hmmm, ‘cucumberYearlyReport‘ looks quite stupid in the address bar. Hold on! We sell mobile phones, don’t we? Who came up with those cucumbers!?”.
It seems someone likes to keep his grocery list in the same excel sheet as the project backlog. We should probably use Mingle (it’s just 60$/month/user). Anyway, the point is that drilling top-down triggered important questions early and let us not waste time on cucumbers.
Some assume TDD = unit tests. That’s wrong. The ‘T’ means any test. Even manual, like typing url in address bar. If there is no no integration/top-level testing facility, don’t worry, test manually and drill down. Sometimes it’s easier to start from a unit test. However tempting it seems, don’t start from the bottom. You may waste time creating something useless (like in my very real-life cucumber example) or something that requires loads of refactoring (like nearly always when you go bottom-up).
Bottom-up is like buying a car audio system before buying a car. Firstly: it may happen that I really wanted a bike not a car. Secondly: I bought Audi R8 and now all I want to listen is the engine. Wrrrrrr. Finally: Oups: the cables don’t fit.
Oh well, I’ll sell this car audio on ebay.
TDD doesn’t need up-front analysis gotcha.
If you write the test first then you exactly know what the code should do and how the interface should look like. Self-designable code through TDD. Nice. One may think that test-driven code will design itself neatly along the process. Usually it will but there are limits: big stories.
Difficult stories are unavoidable – they will happen in every project. They will eat your nerves every time you fix another similar bug that emerged because err… you didn’t think the story through before cutting code. That’s it: big stories need smart people (possibly architects, QAs and BAs) to look at first. Even the best TDD and top-down practices won’t help if some deeper thinking doesn’t happen early enough. Later it may be too late (e.g. to expensive) to do the right thing and well… let’s just raise a new tech debt card.