Why Test Driven Development(TDD)?
This blog starts with a brief definition of Test Driven Development. Then we recall the basic goals of programming and understand how TDD is essential for achieving these goals. Then we’ll see a very basic hands-on example of TDD in Python.
Definition
Test driven development is a style of development where you write the test cases beforehand and then run them. What happens next? You see all your test cases failing as you've not implemented anything. Now you start writing code to make the test cases pass and as all of the test cases pass, you're done. You've come up with a correct version of code at the first place. Now you may refactor the code to optimize it further and make sure that it still works correctly by running the test cases again. Fore more information on TDD, click here.
3 Goals of Programming
Let’s get back to the basics and recall our basic goals while we’re writing code:
- The code should be correct, today and in the unknown future
- The code should be readable for other programmers as well as future You
- The code should be extensible i.e. ready to incorporate new changes without a re-write
Goal #1
We achieve this with TDD as we write the correct version of the code in the first place instead of debugging it later.
Goal #2
Usually we break the test cases into different functions with each of them explaining the aspect of specification they are testing. This makes reading the code easier as you know what the code is expected to do rather than reading the standalone code and guessing what it is supposed to do.
Goal #3
When you have a test suite with a decent coverage. You can extend the functionality and test it regressively to ensure that the existing functionality is not breaking. Then add your test cases to test the new functionality as well.
TDD Example
Most of us start to learn coding with a simple print("Hello, World!")
program. But how do we get to know if we have
written it correctly or not? Well, it's simple, we run the program and see the result.
Then, on a later stage, if we are asked to write a program to find if N is a prime number or not. We build our logic and implement it on code. To check if it's correct or not, we try it on some random inputs. If the output is correct, we assume the program to be correct. And that's where we go wrong. We develop a habit of writing code and then test it manually for some random inputs or some edge cases. As we get experienced with programming, we become so confident that we omit the testing part at all. Below is one such program:
I can assume it to be correct on the basis of my testing. But when I give 1 as input, it fails as shown below:
Had we written test cases at the first place, we could have avoided this bug. Below is the
TDD approach for the same program. For simplicity,
I've not used Python's unittest
module and instead wrote a simple function test_is_prime
to test the is_prime
function.
Notice how we have kept the definition of is_prime
empty on purpose.
Let's change is_prime
definition and run the tests again:
Now fixing this 1 as input problem:
Now we can be confident that our program is working for the mentioned input set. If we ever need to
test the funtion for some more inputs, we just need to add them to test_is_prime
definition. This makes testing the program an automated process which can be repeated n number of
times without any manual intervention. Also, by writing program in such a manner, we avoid the
pain of debugging the program later as we've written the program correct in the first place.
That's why we say:
Testing rocks, debugging sucks.
The source code of the final version of the program that we wrote above is available below. Download and try it yourself:
You can email me any questions/suggestions regarding this blog on my email address or ping me on the networking links provided below in the footer.