One of the mind-blowing development techniques that radically changed the programming world; is the Test-Driven Development.
Writing tests before we start coding? Who will do that?
I must admit that I personally wasn’t really convinced by the idea; maybe because I didn’t quite understand the reason we should write our tests first and the way we should do it. Can you have a bad Software Design with TDD? Can you break your Architecture with TDD? Yes! TDD is a discipline that you should be following. And like any discipline you must hold to a certain number of requirements. By the end of the day, it’s YOUR task to follow this simple mindset.
In this article, I will talk about the Mindset introduced by Kent Beck when writing in a Test-Driven Development environment.
Too many developers don’t see the added-value about this technique and/or don’t believe it works.
"Testing is not the point; the point is about Responsibility"
Because so many of us don’t see the benefits of TDD, I thought it would make sense to specify them for you. Robert C. Martin has inspired me with this list of benefits.
One of the benefits is that you’re certain that it works. Users have more responsibility in a Test-Driven team; because they will write the Acceptance Tests (with help of course), and they will define what the system must do. By doing so, you’re certain that what you write is what the customer wants.
The amount of uncertainty that builds up by writing code that isn’t exactly what the customer wants is called: The Uncertainty Principle. We must always eliminate this uncertainty.
By writing tests first; you can tell your manager and customer: “Yes, it will work; yes, it’s what you want”.
Before I write in a Test-First mindset; I always thought that my code was full of bugs and doesn’t handle unexpected behavior.
Maybe it was because I’m very certain of myself; but also, because I wrote the tests after the code and was testing what a just wrote; not what I want to test.
This increases the Fake Coverage of your code.
So many developers are “afraid” to change something in their code base. They are afraid to break something. Why are they afraid? Because they don’t have tests!
“When programmers lose the fear of cleaning; they clean”
- Robert C. Martin
A professional developer doesn’t allow that his/her code rots; so, you must refactor with courage.
Tests are the lowest form of documentation of your code base; always 100% in sync with the current implementation in the production code.
TDD is an analysis/design technique and not necessary a development technique. Tests force you to think about good design. Certainly, if you write them BEFORE you write the actual implementation. If you do so, you’re writing them in offence and not in defense (when you’re writing them afterwards).
Test-First also helps you think about the Simplest thing that could possibly work which automatically helps you to write simple structured designed code.
When you’re introduced into the Test-First methodology, people often get Test Infected. The amount of stress that’s taking from you is remarkable. You refactor more aggressively your code without any fear that you might break something.
Test-Driven Development is based on this very simple idea to first write your test, and only then write your production code. People underestimate the part “first write your test”. When you writing your tests, you’re solving more problems than you think.
Where should I place this code? Who’s responsible for this logic? What names should I use for my methods, classes…? What result must a get from this? What isn’t valid data? How will my class interface look like? …
After trying to use TDD in my daily practice, if found myself always asking the same questio:
“I would like to have a … with … and …”
Such a simple idea changed my vision so radically about development and I’m convinced that by using this technique, you’re writing simpler code because you always think about:
“What’s the simplest thing that could make this test work”
If you find that you can implement something that isn’t the right implementation, write another test to expose this behavior you want to implement.
TDD is - in a way - a physiological methodology. What they say is true: you DO get addicted to that nice green bar that indicate that you’re tests all pass. You want that color as green as possible, you want it always green, you want it run as fast as possible so you can quickly see it’s green…
To be a Green-Bar-Addict is a nice thing.
Kent Beck Test-Patterns
It felt a little weird to just state all the patterns Kent Beck introduced. Maybe you should just read the book Test-Driven Development by Example; he’s a very nice writer and I learned a lot from the examples, patterns and ideas.
What I will do, is give you some basic patterns I will use later in the example and some patterns that we very eye-opening for me the first time.
When Kent talked about “What’s the simplest thing that could work”, I was thinking about my implementation but what he meant was “What’s the simplest thing that could work for this test”.
If you’re testing that 2 x 3 is 6 than when you implement it, you should Fake It and just return the 6.
Very weird at first, especially because the whole Fake It approach is based on duplication; the root of all software evil. Maybe that’s the reason experienced software engineers are having problems with this approach.
But it’s a very powerful approach. Using this technique, you can quickly get the bar green (testing bar). And the quicker you get that bar green, the better. And if that means you must fake something; then you should do that.
This technique I found very interesting. This approach really drives the abstraction of your design. When you find yourself not knowing what to do next, or how you should go further with your refactoring; write another test to support new knowledge of the system and the start of new refactorings in your design.
Especially when you’re unsure what to do next.
If you’re testing that 2 x 3 is 6 than in a Triangulation approach you will first return 6 and only change that if you’re testing again but then for 2 x 2 is 4.
Of course: when the implementation is so simple, so obvious, … Than you could always implement it directly after your test. But remember that this approach is only the second option after Fake It and Triangulation.
When you find yourself taking steps that are too big, you can always take smaller steps.
If you’re testing that 2 x 3 is 6, in an Obvious Implementation approach you will just write 2 x 3 right away.
I thought it would be useful to show you some example of the TDD workflow. Since everyone is so stoked about test-driving Fibonacci I thought it would be fun to test-drive another integer sequence.
Let’s test-drive the Factorial Sequence!
What happens when we factorial 4 for example? 4! = 4 x 3 x 2 x 1 = 24
But let’s start with something super simple:
Always start with the same sentence: “I would like to have a… “. I would like to have a method called Factorial which I could use to send an integer with that will calculate the factorial integer for me.
Now we have created a test before anything about factorial is implemented.
Now we have the test, let’s start now by making our code compile again.
Let’s test this:
Hooray! We have a failed test == progress!
What’s the simplest thing that we could write in order that this test will run?
Hooray! Our test passed, we can go home, right?
A Bit Harder
What’s next? Let’s check. What happens if we would test for another value?
I know, I know. Duplication, duplication, duplication. But were testing now right, not yet in the last step of the TDD mantra.
What is the simplest we could change to make this test pass?
Yes, I’m feeling good right now. A nice green bar.
One Step Before Generalize
Let’s add just another test, a bit harder this time. But these duplication is starting to irritate me; you know the mantra: One-Two-Three-Refactor? This is the third time, so let’s start refactoring!
Ok, what’s the simplest thing?
Ok, we could add if/else-statements all day long, but I think it’s time to some generalization. Look at what we’ve now been implementing. We write 24 but do we mean 24?
Remembering Factorial, we mean something else:
All still works, yeah. Now, we don’t actually mean 4 by 4 do we. We actually mean the original number:
And we don’t actually mean 3, 2, and 1 by 3, 2 and 1, we actually mean the original number each time mins one. So, actually the Factorial of the 3! could you say, no?
Wow, still works.Wait, isn’t that if-statement redundant? 2 x 2! == 2 right?
Now, the factorial of 0, is also 1. We haven’t tested that haven’t we? We have found a boundary condition!
This will result in a endless loop because we will try to factorial an negative number; and since factorial only happens with positieve numbers (because the formula with negative integers will result in a division by zero and so, blocking us for calculating a factorial value for these negative integers).
Again, simplest thing that could work?
Now, the last step of TDD is always Remove Duplication which in this case is the 1 that’s used two times. Let’s take care of that:
Hmm, someone may have noticed something. We could actually remove the other if-statement with checking for 1 if we adapt the check of 0. This will return 1 for us in the recursive call:
By doing this, we also have ruled out all the other negative numbers passed inside the method.
Why oh why are people so skeptic about Test-Driven Development. If you look at the way you use it in your daily practice, you find yourself writing simpler and more robust code.
TDD is actually a Design Methodology and not a Development Methodology. The way you think about the design, the names, the structure… all that is part of the design process of your project. The tests that you have is the added value of this approach and makes sure that you can refactor safely and are always certain of your software.
Start trying today in your daily practice so you stop thinking about: How will you implement it? but rather:
How will you test it?