Story excerpt provided by Patriot Software.
Written by Stephen Banks.
Test-driven development (TDD) is a set of principles for developers to use when they’re writing code, wherein the coder writes tests before the production code. It’s a pretty simple and straightforward process, really. So simple in fact that it’s a wonder why more coders don’t do it! At Patriot Software, we use TDD to create our accounting and payroll software, and we have gleaned development insights worth sharing.
At its essence, Test-Driven Development is all about writing no more and no less code than is needed for your process to run smoothly and efficiently. To ensure this, a coder starts by writing a fail test for his code first. This acts as a framework or guideline for the production code to follow. The alternative is free-form coding first, which can be wasteful and inefficient.
The 3 rules of TDD
Speaking of efficient, if you’re a software developer and you follow these three rules, you’re doing TDD.
- Rule number 1: You write a fail test for your production code, then only write enough production code to pass your fail test. The only other time you write production code is when you’re refactoring, which is changing the structure of the code, but not the behavior.
- Rule number 2: You only write enough code to ensure your code fails your test, proving the failure test works.
- Rule number 3: You only write enough production code to make the test pass.
Seems simple, right? It is. What you’re doing is defining a target, producing a way to ensure you hit your target, then finding the most efficient way to hit your target over and over again.
Test-Driven Development example
Let’s say I call a function and the result should be five. Because I haven’t written the code to give me a five yet, the code will automatically fail because that function doesn’t exist. That would raise a compiler error that says, “No, you can’t call that function.”
So, I create the function. After I do that, the compiler says, “You created the function, but it doesn’t return five.”
Then, I write the code to return five. The compiler is happy. I’m happy. My fellow coders are happy. On I go to the next code block.
The prewritten test determines what the code should do. At first, the test fails because the function doesn’t yet know what it’s supposed to do. This failure guides me to my final destination, informing me over and over again that my job is to make sure the production code passes the test. Thus, my production loop will be: write a fail test, make an assertion, code that assertion, pass my test, repeat. And I keep repeating, working on small pieces of functionality at a time, until I’m done.
A big bonus here is that, in the end, your tests completely describe the behavior of the module of code you wrote. TTD ensures every behavior is defined by the set of tests, which are easy for other coders to understand.
Advantages of Test-Driven Development
Test-Driven Development is a great way to code, especially when you consider the alternative, which is basically saying, “I’ll just write the code first and do the test later.”
When you test after writing the code, it’s easy to skip parts of the code that need to be tested. After you finish a project it’s natural to want to move on to something more interesting. Testing after writing the code actually requires more analytical thought than testing as you write.
But a lack of motivation to find mistakes isn’t the only thing developers should look out for. When you write code without testing, there’s fear involved. You think, “I really want to change this class, but I can’t because I don’t know what’s going to break!”
If you’re not sure what an adjustment will do to the code, you’ll spend a lot of extra time tumbling down the code rabbit hole, changing classes with different methods, wondering if what you did worked or just made things worse.
TDD gives you the confidence to go into the codebase and add to it. If you break something, the test will tell you it’s broken. A suite of tests is really a safety net. With these tests, you know your code is in a working state.
If you’re just exploratory coding without tests, you’re not sure what code you need before or after it’s written. You may not catch the code that’s unnecessary, and the unnecessary code doesn’t add value.
Finally, TDD forces you to have decoupled code. This is great because you can’t test all the code’s behavior if it’s coupled. When the code is coupled, the behavior of one module is completely linked to the module it is coupled with. Coupling requires a change in both modules. That means that if you test one module and find a problem, you have to change both it and the module it’s coupled to. But with TDD, you’re forcing the modules to be decoupled, thus avoiding all the inherent coupled testing issues.
Conclusion: Implement TDD for efficiency
Testing as you write code might seem like it will slow down your process. But, you will spend more time later fixing the code that doesn’t work. Then, you have to figure out why the code is not working and debug it. In all likelihood, test driving will save you time. And, it will save other developers when they need to change a module you created.