Test driven development is mystical to anyone that hasn’t done it from the ground-up. It’s almost like riding a bike; you really don’t know how easy it is and how much fun you can have with it until you get up and going. When the training wheels are off, it then becomes a brand new world full of possibilities.
I have been writing unit tests for years. Having come to the Java game later than most of my colleagues, I really like to make sure that everything I commit to my various development communities are well-tested and as clean as I can make them. Unit testing has allowed me to verify this in two different ways. First, it guarantees that what I’ve writting works, and more importantly, it makes me keep my code simple. Frankly, if I start writing a test and it becomes a dependency-driven, closely-coupled to the implementation monster, I can pretty much guarantee that the code is going to be the same. I’ll try to refactor this and use my tests in this manner as a guage for it’s quality.
So exactly HOW would I begin development by writing a test BEFORE I write any actual code, and WHY would I even want to?
We’ll address the “why” aspect first. “Old School” developers that I learned most of my skills from would create “fake” layers that the code that they were writing would have to talk to. A “fake layer” is defined as some type of data or code that simulates the functionality of an “expected” production-level deployment.
These “fake layers” would be similar to tests, but these guys wouldn’t write tests because they never did in the past (and some still don’t!). Testing just gives you a better framework for creating fake layers that you can create your code around.
HOW do you start with a test?
It’s actually quite simple. Let’s say you will be writing an application that talks to a database that has been created (you could even start without the database and just an ER diagram, but let’s keep it kinda simple). Looking at your requirements, you are probably going to implement CRUD operations against it. You start thinking about what the interface for the Data Access Object is going to look like. Hmmmm — Let’s see — CREATE, READ, UPDATE, DELETE. So your simple Interface has four methods.
Now start with a test. Create a class called MyDAOTest. MyDAOTest will test the Implementation Class for the interface’s methods. When we create a method in the test that tests well, we then create the interface method, then the move the test method, with proper modifications, into the a new MyDAOImpl class. It makes you create very smart tests that ensure you are “CRUD-ing” the data you expect. Coding in this way also creates the implementation to your interface at the simultaneously with the test. Finally, it makes you think about the interface and the implementation, and affects your overall design in a very positive light.
Now let’s take it further. You’ve created all the methods in your Impl class and your Interface methods can be extracted and the actual interface is now written and, for the time being, complete. Now let’s write a test that calls the Interface and implements a new Implementation Class through it. You can now see how clean your Interface is — how easy are your methods to use, how are things being cleaned up, and finally, how are exceptions being caught? Are you satisfied with the results so far? If so, proceed to the next layer!
The next layer may possibly involve a creating a Session Facade to allow for user interaction with the database (simple implementation, obviously). You can then create an Object model that translates the Database relationships into some type of presentation-level object gouping, and write tests to reflect this. It becomes very obvious what objects you need when you start writing a test that outputs the data that you want in a conditioned manner. Your first cut at it may look like horrible code! It may be some type of monolithic monster, but then you can see how things need to be split up. You keep splitting things up and writing tests around it, and sooner rather than later, you are suprisingly code-complete!
Do what works for you.
Even after all this I’m sure that some peoople are completely poo-pooing what I’m saying. That’s fine if it works for you. I’ve found that even if I begin by writing code to tests and continue writing tests and interfaces and all kinds of extra, “non-production” code, my productivity jumps dramatically as the project continues because I write less and less code as the project nears completion.
It’s about making everything solid. Using TDD, you’ll be writing one line of code to your previous ten, and when your “business guy” walks in and asks for a change, you’ll be going home at 5 instead of having your car languish in the parking lot after midnight. You’ll also have more confidence in the code that is checked in, and when things go wrong and the inevitable finger-pointing starts, you won’t be a pointee!