2016/02/22: Test-Driven Development: The Key to a Good Night's Sleep
Imagine joining a company where you are replacing a programmer who has left you with a decade's worth of code. The code has all sorts of flags and features. Your new boss is anxious to quickly get you up to speed so you can start modifying the code and adding new features. There's only one problem--one MAJOR problem. The code has no test cases. There is no [easy] way to ensure the code isn't already broken from a feature the previous programmer may have been working on. There is no [easy] way to ensure the modifications you are about to introduce aren't going to break all kinds of other features used by other systems downstream. Sounds like a nightmare, right? Well, it's worse--it's reality. All too often, programmers and companies have left the act of writing tests for their code for the very end, or for when the code needs to be modified. The above nightmare scenario would greatly have benefitted from the concept of Test-Driven Development.
In today's blog, I'll be discussing a high-level overview of the concept of Test Driven Development. It is said that TDD is what allows you, the programmer, to sleep at night. It allows that warm fuzzy feeling that the code operates now, post feature implementation, no different than before, albeit with the new feature added to it. So how does one use TDD to keep the aforementioned nightmare from becoming reality? It all starts at the beginning.
The concept of TDD is credited to Kent Bock. The idea is that before you begin coding, you must first clearly understand the program requirements, through either use cases, user stories, requirements docs, etcetera. You will then apply a 3-stage cycle known as "RED" > "GREEN" > "REFACTOR" and is summarized as follows:
- Write a new test
- Confirm all previous tests pass and the new one fails (RED moment)
- Do the minimum amount of code necessary to make the new test pass (GREEN moment)
- REFACTOR the code and ensure all tests remain green
- Repeat cycle until your project is done
The key is that the following has to be done FROM THE BEGINNING of the project so that you have your series of prior tests to ensure the program hasn't broken. In addition, making the new test fail initially is important to ensure that it CAN indeed determine failure. (What good is a test that always passes?) This is your chance to confirm it catches errors. And then you add as little code, however inefficient or absurd, to make the test pass and not break previous tests. Once passed, you can then safely refactor the code as needed and have the tests to prove your refactoring hasn't broken the program. If it does, you will rely on your source control system to revert.
If it isn't already clear, I will spell out the benefits of this process. (1) This iterative development process, according to proponents of TDD, makes programmers more efficient as it promotes rapid development with little to no downtime from tracking bugs (as the constant testing should in theory rule out bugs from the get-go), (2) inherently produces stable software and (3) because of all the test cases already written, doesn't leave the possibility of tests being an after-thought for the end of the project, when most likely it will never get written due to project deadlines and new workloads pushing the tests to the back-burner indefinitely. With the tests already written, the program is already future-proofed to catch errors that will inevitably be introduced in future modifications.
Think back to the initial nightmare scenario I brought up, taken from a real life situation from my personal experience. It is clear to see how that scenario would be much less intimidating had TDD concepts been applied. Obviously this is just a high level overview, but you should be able to get the gist of why applying TDD principles to ones coding is of extreme importance. Maybe you'll finally have peace of mind in your future releases. Until next time. -- JLH