TDD: A Practice to (Hopefully) Prevent Bugs

Pande Ketut Cahya Nugraha
pepeel
Published in
5 min readFeb 24, 2020
Taken from https://www.reddit.com/r/ProgrammerHumor/comments/f8nnug/code_bug_p_p/

Bugs. The bane of every software engineer since the dawn of software development. Numerous methods have been devised to prevent it and one of the most famous (and arguably effective) methods to prevent it is Test Driven Development or frequently abbreviated as TDD.

What is TDD?

TDD arise from a very simple concept that many of us simply forget before coding. Think first about your code before implementing it. What it could do, what could go wrong, et cetera. All of those scenarios are put into a concrete form called “test”, which could be run to… well, test your code, to ensure that it behaves as expected.

TDD is implemented by following this sequence:

  1. Create a test first. By creating a test first, we are forced to think about how our code will work, what is its input and output, and what could go wrong with our code. Typically, we commit this test and make sure that the test fails (as a mark that what we want to implement and test with the test doesn’t exist). It is usually called the RED stage since fails are usually marked with red colour.
  2. Create an implementation that will pass the above test. Usually called the GREEN stage since success is usually marked with green colour.
  3. If needed, we refactor the code to improve performance or readability. Usually called the REFACTOR stage which the reason for should be obvious.

Pros and Cons?

As with many things in this world, TDD has its pros and cons. While it is arguably a good practice, TDD is not applicable everywhere. Before applying it, you should consider its pros and cons.

The most obvious pros are that it detects if your code can pass both good and bad scenarios. Sure, it cannot determine if your code can smoothly pass unexpected scenarios, but at least it will ensure that your code can pass any scenarios that you as a software engineer thought of. Another pro is that on refactoring, you can make sure that the code still works as intended. Tests also work as a kind of documentation, documenting the input, output, and expected use of your code. Lastly, the mere fact that you are forced to think of all the scenarios your code can go through makes your code less prone to bugs compared to mindlessly and recklessly coding.

Unfortunately, TDD is cumbersome. For some part of code, such as front-end, which can go through numerous requirements changes, making tests every time those requirements change is simply not feasible.

How pepeel team use TDD in PPL 2020

PPL 2020 enforces its participants to use TDD in software development, which is represented by our commit messages that have to be prefixed with [RED] [GREEN] [REFACTOR] tag and the requirement that our code coverage must reach at least 90%, which is calculated using the tests. Here is one of the examples of how we use it.

A typical RED-GREEN-REFACTOR sequence (without the REFACTOR)

As you can see, before implementing a new feature, we create a failing test and push it to the repository to confirm that it is indeed a failing test. Here is the screenshot of the test that I make for the commit shown above.

A simple positive-negative test.

As shown above, I created a test for two scenarios that my Modal component can go through, in which the Modal is shown and not shown. This follows one the good indicator of a test, which is testing at least one positive scenario (in this the one where the modal is shown) and one negative scenario (where the modal is hidden).

After we implement the Modal component, we can see that it passed the test as indicated by the green checkmark on the pipeline status. In the future, we can refactor the component and be ensured that it will still work as intended as long as the test is passed.

How do we feel about TDD

I think I can speak for the whole team when I said that TDD feels very, very cumbersome. We also very often jokes that we spent a lot more time creating tests compared to actually coding the features. On average, for a feature that took about 3 hours, I spent 2 hours creating tests and 1-hour coding the actual feature.

However, while we resent the time wasted creating tests on the first weeks of developing, afterwards, we began to actually feels the benefit of TDD. It became more apparent when we tried to integrate our work. When we merging our works, several merge conflicts emerge, and after resolving that, the tests are crucial in determining if we resolve that correctly, as missing code will automatically fail our tests. Furthermore, when we move on to the next sprint and start fixing bugs that appear on our app, the tests once more shows its use by determining if our fix change the behaviour or merely fix the bug.

In conclusion, I feel that TDD is worthwhile on a project that involves many people such as Document Builder. In a way, the tests we create serve as a kind of documentation of what our code should do, and thus can serve as a warning for us if we inadvertently change our code to not do its original function. By using TDD, we can confidently move forward in coding. As one of my teammates said, 100% code coverage will not prevent all bugs, but it still can reassure us that most of the bug we think is already prevented, and we will not introduce more bugs in the future because the tests safeguard our past codes.

--

--