For around 3 years ago I came over the TDD approach and got very excited by it. It seemed very logical in lots of situations to me. I was doing once a presentation about it and want to discuss in this blog post what I have learned after 3 years applying it where ever I could.
First of all, here is the presentation summarising the theory and the procedure itself.
If you would ask me today if all the theory I read in the books and presented applies, I would answer, yes… in the most cases. Maybe the most important thing to keep in mind is the Return Of Investment (ROI) and this is how the most project managers and architects want to see it which is logical.
What got me interested the most in the first place is the following principle included in TDD.
Lets look at the traditional waterfall model first:
Important here is to mention, that for a developer this is not only the way how the project process mostly works, even in an agile sprint cycle. It is the workflow he goes through writing any small feature. An example here would be a service method a developer would have to write in a spring architecture, that is consuming injected DAO Instances.
TDD comes and suggests a development workflow in the following way:
Notice the position of “What?” and “How?” in this case.
What is so special in this workflow change and what did inspire me here so much?
Well two things:
1. Design only if needed
Not in a huge procedure upfront not knowing if it will work or not.
Sit down with a beginner developer and analyse how he approaches a problem he has to solve. One path he could take is sitting down and trying to draft his stack of methods on a piece of paper and we know the UML approaches we have been though at school. This is actually the typical classical view of a task for an architect, that never worked in the reality.
The other way is to start implementing using TDD and designing your methods stack along the way.
Yes we need the big architectural design on the level of system APIs, your REST communications to other systems. But on the code level its hard to see your design upfront and its not necessary to do it. This is the basic though of agile development how I learned it from Martin Fowler.
As Martin Folwer describes in this article, the waterfall procedure is predictive and therefore completely hard to execute, since we want to predict things and grasp a huge system that is very hard to grasp.
The agile process is adaptive. Meaning that we go driven by the features we have and design along the way, adapting it to our requirements, asserting the functionality through the test. This is the reason why no agile procedure can be done without the test.
Does your manager knows it? Ask him how he defines agile? Besides doing your requirements in short loops of two weeks.
2. Test = Specification
This is the rule lots of people who don’t like TDD don’t understand.
You ask them what does TDD means for you?
“Oh well this is the approach where you just write the test first… total nonsense for me, how should I know what my code will look like upfront…”
Yes it is nonsense if you do exactly the same what you do in the classical “test at the end” approach.
As I looked at the fact ” test = spec “, the main though coming into my mind is that “test” in general, is the wrong word to use, once we are using TDD. We don’t have a test anymore since a test is something that you apply to some product you have already produced.
What we are talking about here is an “automatically executable specification” of our code. This is the most important point to understand and all other principles follow after it:
- Having written the test as the specification
- Focusing on one specific part of your system at a time
- Holding one part of your code in your mind while writing the test for it, it goes along with something that I call “do it properly while you know it, then you will forget it”, what is easier to write your test as you write the code or some weeks later once you forgot it?
Now to the other part of this discussion.
Is TDD valuable to us and should we use it ?
While the most employers want you to apply it, at least as you see it in the job postings, there are still folks out there that you need to convince to do it and I see them again and again. Thats why I want to discuss it here again.
There is a reasons why not any developer is adopting TDD. I have been experiencing the following yet:
1. Developers just don’t understand TDD
They are not passionate about it. They do some mix of TDD and Waterfall Testing without clearly using the advantages of TDD.
The solution here is to clearly experiment with some samples and try to apply the principles. Go though my presentation at the beginning of the post. You can work though 3 cycles in TDD, apply the Eclipse short keys for your code generation and try to understand what is each step bringing to you in value while programming.
2. Test setup is time consuming.
This is the biggest enemy of TDD and I my self was trapped in it several times.
What you have here is mostly a very complicated business domain combined with a tedious fixtures setup.
The reason for it is usually a bad technology choice and a wrongly given fixtures environment.
The technical lead of the project should care about the degree and the time investment for a test setup. If needed, a dedicated person should be assigned the task to create an easy test setup for any developer and a good base for your tests.
Here are my critical questions to ask and solutions coming along with them:
a) Do you have textual DSL fixtures? Like XML, CSV or YAML (Rails)?
Well, don’t expect your team to reach a good test coverage then, you have lost it for a big part. Yes a good developer can survive with restricted testing and I worked with developers like that. As you know though any code and any coding procedure gets complicated soon enough, your goal is to make it as simple and as productive as possible and this is your competitive advantage.
Back to fixtures…. A huge improvement usually comes by setting up an API to create fixtures and this is what helped me extremely in several projects to reach a good test coverage.
You can see movements in the java and rails community that prove it big time:
- Java’s POJOs term, objects you can create easily in any testing environment
- Ruby’s libraries like factory girl , an API to create your objects for the test
And by the way, by creating your objects programmatically and not with fixtures you strongly force the developers to simplify the object creation. You create two clients of your code immediately. The test and the code you will use your objects in.
b) Besides the fixtures, is a general test setup easy? Do you need a bunch of knowledge to setup a test?
The last thing I want to discuss is the ROI. This principle applies in any testing environment, doesn’t matter if its easy to setup your test or not. It should be your first priority rule to decide about going with TDD in a certain situation or without it. Potentially you can motivate any new method of your code with a test, to follow strongly the TDD principle but its not always the best thing to do.
What you should go for, is obviously highest ROI. Basically reaching the most value out of your test by investing the lowest amount of time.
For my self I experienced that something that I call “basic code execution” as a test and some basic assertion bring already a huge value with them. This is how I perceived the profit of TDD just by applying this rule.
Have you ever heard the saying “Better some test than no test at all!”, this goes along with this experience and it is absolutely true. I would say you reach definitely over 50% of possible profit out of tests by just having the code execution.
What does a basic code execution bring you?
- Testing bean configurations in a spring framework architecture.
- Indirectly testing your code areas serving your new feature.
- Designing your APIs how you want them to be up front.
- Focusing on one part of code instead of starting a debugging session in a method stack.
- Testing a controller in a Rails environment renders and tests the belonging view with it.
So apply the minimum amount of work and get already a huge value from it.
These are my general thoughts on TDD after using it for some time. I would say its still a great concept, I love to apply it and can only recommend it to all the others.
My question to others would be:
If you know TDD but don’t apply it, what is your reason for it?
What is the highest possible test coverage you have ever reached in a real project?