After working on multiple projects, I was able to see what difference writing tests make. Sometimes, it seems like writing tests would just eat up a bunch of time and that it's good to test features yourself. But as the project gets bigger and more complex, it's harder to make sure that the code you wrote doesn’t affect other parts of the project.
One project I worked on had many features that affected each other, and our client wanted some significant changes that could break other features. Shortly after, bugs came along, and at this point, we couldn't recall why something was written the way it was and where else it was used. After implementing the change you have been asked to, you test it, and everything seems okay - or so you think. Suddenly, someone reports that other parts of the application are broken, and you find yourself wasting hours on something a test would notify you about in seconds.
This happens a lot. Programmers are being pressured to ship code as fast as possible and often don't push back and negotiate realistic estimates. And then you start to hear, “TDD is a waste of time because I can’t code that fast.” But in reality, you would have code that is easier to refactor and be sure that changes won’t affect the project. You'd spend far less time debugging.
So, what is Test Driven Development?
TDD is an innovative software development approach in which you write tests before writing the bare minimum of code required for the test to be fulfilled. In simple terms, test cases are created before code is written. The purpose of TDD is to make the code clearer, more straightforward, and bug-free. TDD starts with designing and developing tests for every small functionality of an application. The simple concept of TDD is to write and correct the failed tests before writing new code. This helps to avoid code duplication as we write a small amount of code at a time to pass tests; TDD sometimes is also called "Test First Development."
Why use TDD?
The short answer is “because it is the simplest way to achieve both good quality code and good test coverage.”
The longer answer comes from what TDD really is.
For start, you need to follow these rules:
You are not allowed to write any production code unless it is to make a failing unit test pass
You are not allowed to write any more of a unit test than it is sufficient to fail, and compilation failures are failures
You are not allowed to write any more production code than it is sufficient to pass the one failing unit test.
The rules define the mechanics of TDD, but they are definitely not everything you need to know. The process of using TDD is often described as a Red/Green/Refactor cycle.
The red, green, refactor approach helps developers compartmentalize their focus into three phases:
- Red – think about what you want to develop
- Green – think about how to make your test pass
- Refactor – think about how to improve your existing implementation
The red phase is always the starting point of the cycle. The purpose of this phase is to write a test that informs the implementation of a feature. The test will only pass when its expectations are met.
For example, imagine you want to create a function called sortArray that sorts the numerical values of an array in ascending order. You may start by writing a test that checks the following input and output:
Input: [2, 4, 1]
Output: [1, 2, 4]
When you run this test, you may see an error message like:
As you can see, the purpose of this phase was to define what you want to implement. The resulting error message from this test informs your approach to implementation. At this point, you are considered “in the red.”
The green phase is where you implement code to make your test pass. The goal is to find a solution without worrying about optimizing your implementation.
In our sortArray example, the goal is to accept an array like [2, 4, 1] and return [1, 2, 4].
You can approach the problem by writing a loop that iterates over the array and moves the current number over if it is larger than the number to the right. Then nest this loop inside of a loop that repeats until all of the numbers are sorted. After implementing this, you should see a passing test message that could look something like this:
At the end of this phase, you are considered “in the green.”
Now, you can start thinking about optimizing your codebase while having a descriptive test if you do something wrong. In the refactor phase, you are still “in the green.” You can start thinking about how to implement your code better or more efficiently. And by that, I mean how to accomplish the same output with more descriptive or faster code.
For example, instead of using a bubble sort method, you can use the merge sort algorithm because it has a faster average sorting speed than bubble sort.
So, after refactoring the sortArray function and running the test, the test message would look something like this:
Do you notice the difference? Instead of 19 ms, the code now needs only 7 ms to execute.
Summary
In test-driven development(TDD), a test is written first before writing the functional code. This doesn’t mean that TDD can replace traditional QA, but instead, it ensures effective unit testing. An effective unit test will ensure better code coverage, fewer bugs, and QA efforts in the long run. Implementing TDD can be challenging though, because you need people involved who have the necessary skill sets for using different unit test frameworks. If you have the right people, then it’s always good to implement TDD in your projects.
Conclusion
While it’s challenging to learn to use TDD, in my opinion, every developer should be familiar with it. Because in the long run, TDD is definitely not a waste of time, and is a way to keep code clean and optimized. Refactoring code could also cause other parts of the application to malfunction without us knowing. Then, you might have a situation where the project is “finished,” but you get a message from the client saying that something broke, and it is your responsibility to fix that. Most likely, you are already on some other project, so now you have to work on two sides, probably doing overtime, and at that moment, you realize that if you had written tests, this wouldn’t have happened. You already wasted more time on solving bugs, than just writing tests from the beginning.
But what if you are working on a small app, that has few features and is not that complex. Is there a need to write tests for it? No, it's up to you or your team to decide if TDD will take more time or save it.
The bigger the app, the more likely TDD will save you time and stress.
Aleksandar Tišma
Top comments (0)