Test Driven Development (TDD)

Test, code, cry, repeat.

Johanes Jason
8 min readMar 19, 2022

“Write tests until fear is transformed into boredom” — Kent Beck, Test-Driven Development: By Example

As a developer, we are required to create a perfect app. Although in real life implementation this is impossible to achieve, we should at least tried to make an application that can work properly. That’s why we should test our program. When we test a program, we want the program to run according to the scenario we made in the test. If the test is successful, we can be sure that our program is running perfectly. In the Software Engineering Projects course that I took, we use Test Driven Development (TDD) as the test methodology.

What Is Test Driven Development?

TDD is a software development process relying on software requirements to be converted to test cases. That means, we should write the test first, before writing the actual code. TDD forced us to think about the program first before writing the code. TDD has 3 commit milestone or phases :

  • [RED] is a phase where we write tests about the features or programs that we will create. In this phase, we write the successful and failed scenario, so our code can handle every possible scenarios. If we want to use any database on our program later, we can mock the object first.
  • [GREEN] is a phase where we write the implementation code, where this implementation must pass all the tests that we made earlier. What we should pay attention to is the implementation we made must also passed all old tests that we or our friends made.
  • [REFACTOR] is a phase where we write a better version code. We can implement clean code, use faster data structure or algorithm, or whatever it could be, but note that the functionality and maintainability of the program does not changed.

Advantages And Disadvantages of Test Driven Development

Nothing is perfect, neither the code we made nor the test methodology we use. TDD has its flaws, but it can also helps us.

Advantages

  • Only write necessary code
    Because we write our test first, that means we already know what to write on our program. When our implementation already pass the test, we don’t need to write undesirable code, because the code we wrote already the simplest code possible (after refactoring, of course).
  • Easier to maintain
    In TDD, we maintain one microfeature at a time. Because the low coupling aspect, we can maintain our microfeature without affecting another feature. If we use TDD, we can completely change our implementation while using the same tests, and if we passed the tests, our job is done.
  • High test coverage
    High test coverage means our lines of code are covered by the tests, which means if the test coverage is high, we could say that the lines of code covered by test are working properly.

Disadvantages

  • Slow
    TDD is slow. We are required to understand the software requirements, write the tests, and run the tests before actually writing the code. It’s actually not very hard if we work on small projects. But if we work on big projects, we will need a lot of time to think about the test, more than the time we need to work on the implementation.
  • Tests have to change if software requirements changed
    If your team use scrum methodology, your team must have a Product Owner. I will explain about Product Owner on another article, but what you should know is Product Owner have a role to represent our client. Product owner will review our application after it is done, and gives feedback about the shortcomings of this application. And then, if the requirements changed, we need to change our application, which means we also have to change our test first. So, this disadvantage is the same as the one I wrote before, TDD is slow.

TDD Implementation on My Team Project

In our team project, I’m working on authentication feature, including register, login, and change password. Our team use Django Rest API as the backend framework. Here’s the example of my TDD implementation on sub-feature Account Register.

[RED] Phase

First, I created a mock for setup my user model.

Setup for endpoint and data user

On the setup, I want my user model to have username, password, and role for its attribute. This user isn’t a superuser so it don’t have any admin attributes. Next, I create a class to test when I successfully and failed to create a model.

Test when successfully and failed to create a user

In the first function, I created a (UserAccount) model based on data I prepared earlier. Then, I want the model’s attribute to be exactly the same as the data earlier, with checking the output with assertEqual. In the second function, I intentionally removed username from the data, and hoped for an exception for return.

[GREEN] Phase

What do I do next? To implement the test, I should first create a User model. I create an AccountManager and UserAccount models, so I can create a user with ‘admin’ role later.

AccountManager models
UserAccount models

I won’t go much into details, what you should know is I create User models with attribute based on requirements just like what I wrote on tests, like username, password, and roles. With this, when I ran the test, the models will be created and it will have the same attributes as the data on tests. Here’s the pipeline test on Gitlab to show that the pipeline failed on [RED], and success on [GREEN].

Failed pipeline on [RED]
Success pipeline on [GREEN]

[REFACTOR] Phase

In this phase, I implement clean code with sonarlint from Visual Studio Code for python. I will talk about clean code later on another article. Here’s the screenshot from Gitlab about my clean code implementation.

Refactoring clean code

In the screenshot, the line that has red block is before changes happen, and the lines with green block are after the changes. In this case before I change the code, the line is too long. It is one of the principle of clean code. So I changed the code so that the parameters of the function have their own line. We should implement clean code, because the one who reads our code isn’t only us.

If you want to know how I implement linting from Visual Studio Code for python, here’s the tutorial I use.
https://www.youtube.com/watch?v=eMIxokGhFHM

Besides clean code, you also want to check for bugs, security of your program, code smell and duplication, and so on. To handle those things, we can use Sonarqube. We can run a Sonarqube with docker, and then scan our local environment. I will create an article about Sonarqube later. Here’s the screenshot of my Sonarqube.

As you can see, Sonarqube shows coverage, duplication code, code smells, security warning, vulnerabilities, and bugs. With this, you can make sure your code is clean, safe, and effective. All of this process is done in [REFACTOR] phase.

TDD in Frontend

In this article, I mostly talk about implementing TDD, and the example how I do it on my backend feature. But how about frontend? My team use React JS as frontend framework, and to do unit testing on React, we use react-testing-library. But how do you exactly unit testing a frontend app?

Based on my personal experience, if you want to test a frontend app, you want to check “if I click this button, what will happen or where will I be taken?”. I honestly don’t know if this really the best implementation of TDD of frontend, but this is how I done it.

First, I always create my page design on Figma, or if your team already have UI/UX designer, they will give you design of the page. In this case, I want to create a navigation bar for my page. Here’s the design I made.

Navigation Bar Design

So for example, what I want to test is “If I click on Profile dropdown, and click on Logout button, I will logged out and will redirected to Login Page”. I already know that I want to use React Bootstrap library for navbar, and I already read about the documentation, so I know that the “Profile dropdown” is an anchor element, wrapped by Nav element. So I must get the Nav first, get the anchor element, click it, get the Logout button element, click the Logout button, and then the page will redirected to Login Page. So this is the test that I made.

I won’t explain about the syntax or how react-testing-library works, but you can see that the test that I made follow the process that I wrote earlier. For the implementation, here’s the code of Profile dropdown element.

Profile Dropdown Element

As you can see, if we click on Logout element, it will send us to ‘/login’ endpoint.

I don’t really know the correct implementation of TDD in frontend, this is the first time ever I tested a frontend app, I cried in the process, but who doesn’t? Anyway, that’s my explanation of Test Driven Development and how I implement TDD on my backend and frontend features.

That’s all from me, thanks for reading!

References :

--

--

Johanes Jason

A normal college student from Faculty of Computer Science, University of Indonesia