TDD: The Sacred Cycle

Muhammad Ridho Ananda
6 min readApr 26, 2020

--

https://blog.usejournal.com/test-driven-development-understanding-the-business-better-9c4cae4cb990

Overview

Test Driven Development, as the phrase “test driven” suggests, is the practice in software development where developers write test cases before production codes. The test cases written are used to specify and validate what the code will do. The purpose of TDD is to make the code clearer, simple, and bug-free.

Benefits of TDD

Now, you might be wondering why do you need to do TDD when you can just write your implementation codes straight away. Well, if you are doing a personal project or your project isn’t too big maybe you don’t have to. But imagine if your project is big enough, and one day you found some bugs in your code. You decided to fix the bug in your code. How to ensure you didn’t break the functionality of your code? You might want to test it manually but of course it would become tedious as the project grew.

The above scenario is only one of many cases when TDD can be a great solution. Below is some other benefits you can get:

  • TDD automate the tests, therefore saving a lot of time compared to manually testing the functionality.
  • With TDD, you write the test in the first place to ensure the functionality of your code. Whenever you write your implementation code, this test will validate whether your code works as expected. Therefore, TDD makes sure bugs don’t occur in the code.
  • Because tests are in place upfront, time allocated for regression testing can be reduced considerably. Regression testing is defined as a type of software testing to confirm that a recent program or code change don’t affect existing features.
  • Tests written ahead of time will also ensure good code quality.
  • TDD encourages to write minimum code changes. Therefore, it ensures a good code coverage. Code coverage is the percentage of code which is covered by the automated tests.

TDD Rules

Now, let’s get into the technical issues of TDD! Robert C. Martin, the writer of Clean Code Book described TDD with three important rules:

  1. You are not allowed to write any production code unless it is to make a failing unit test pass.
  2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
  3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test.

Or in a simplified version:

  1. Write only enough of a unit test to fail.
  2. Write only enough production code to make the failing unit test pass.

This simplified version is explained here.

The Sacred Cycle of TDD

The rules of TDD can be grouped into three phases. These phases are also known as the Red-Green-Refactor phase and can be visualized into a cycle. The cycle can be seen on the image above. I call it the sacred cycle because it is mentioned everywhere when someone talks about TDD. Okay, let’s dive deeper into each phase!

  • Red phase

This phase is the core of TDD. In this phase, we write test cases before the production code implemented. Write the tests as if the code had already been implemented. However, in this phase you won’t do any implementation for your production code. Therefore, the test cases you have just written should be failing.

Also, it is in this phase where you concentrate on writing a clean interface for future users. This is the phase where you design how your code will be used by clients.

  • Green phase

This is probably the most exciting part of all phases. In this phase, we will write our implementation to pass all the test cases we have written. That is why this phase is named as the green phase.

One important note here is to follow the “write only enough production code to make the failing unit test pass” rule. If you just write the entire code in your mind, then you aren’t doing TDD properly. The reason why you should write minimal code is to minimize bugs and avoid mixing up tested and untested code.

Also, in this phase it is still allowed not to follow clean code best practices. This will be fixed during the refactor phase.

  • Refactor phase

In this phase, your task is to improve your code, while ensuring all the test cases still successfully passed. Improving code here means you should make your code quality better by following clean code best practices. For example, you have to remove code duplication.

Note that because we have written test cases in the first place and keeping our code to pass all of them, we are sure that the refactoring process doesn’t break our code functionality. Based on my experience, this is the most important benefit from TDD.

TDD Implementation (With Example in Django)

Now, let’s get our hand dirty. Currently, I and my four other friends are developing a mobile application using Flutter and Django for the backend side (you would have known it if you had read my previous articles :) ). Because the backend side involved a lot more logic and therefore more unit testing, I’m going to write my TDD experience with Django.

For example, one use case of my application is to create a user data whenever there is a user sign in for the first time. The sign in process is handled by Firebase Authentication, so no unit test is needed. Now, we are left with the use case of creating a user data.

First of all during the red phase, we have to think as if the code is already implemented, so we need to think what is a user would look like. Based on the other use case, we decided that a user would be identified by email. The user will also have “coins” and “hearts” attributes. Great! Now how about creating the user? One solution is using Django REST Framework to create a Web API. This API will return a JSON file response whether a user is successfully created or in case when the user is already exists, failed to do so. So, we need two create two unit test, one to handle successful scenario and the other one for the opposite.

Below is the snippet of our tests.py file which handle these tests. If we pushed this code to GitLab, the CI pipeline would be failing at the unit test stage.

a red cross at unit test stage indicating a failing unit test

Okay, now we have done the red phase, on to the green phase. First, we wrote our user model in models.py file.

And then in our utils.py file, create a utility function which handles REST authorization using JWT token.

Now, to for the API endpoint routing can be configured in urls.py file and left as exercise for readers :).

The last step is to write production code which implement the tests in views.py . Remember to write minimum code here. Also, make sure we already pass all test cases before committing our code. When we push our code to GitLab, the pipeline CI will show a green color for the unit test stage.

a green check indicating we have passed the unit tests

For the refactor phase, we feel like it is not necessary right now since our code is pretty simple. However, we might need it in the future as the our codebase grows larger.

NB: Most of the implementation here is done by Dhipta Raditya. Also remember to install all of the needed requirements first and configure .gitlab-ci.yml file so that unit testing CI pipeline can run properly.

Conclusion

In our project, TDD helps us to think about the design and flow of our code by writing proper tests. TDD also gives us confidence whenever we finished our implementation because we can detect if our code is not working properly, thus we can detect bugs earlier. This way, we can deliver work faster with confidence. Therefore, if you are working in a project, especially which involved other people, it’s always a good idea to implement TDD.

That’s all for Test Driven Development. Thank you for reading. Happy coding, happy testing!

References

  1. https://www.guru99.com/test-driven-development.html
  2. https://blog.testlodge.com/what-is-tdd/
  3. https://confluence.atlassian.com/clover/about-code-coverage-71599496.html
  4. https://www.freecodecamp.org/news/test-driven-development-what-it-is-and-what-it-is-not-41fa6bca02a2/

--

--