Test Driven Development (TDD) Implementation

Case Study of How Implementing TDD Using NodeJS

Eka Pramudita D
8 min readSep 12, 2020
Photo by Green Chameleon on Unsplash

Hello there! Meet me again! For today, I would like to explain what is TDD. But, if anyone doesn’t know yet about the basic of unit test, please check this post:

If you already understand it, let me introduce to you what are we going to talk about. The first thing is about the early introduction of TDD and how to do it. Next, we will discuss a real case along with code implementation using Node.js. And the last is a final conclusion about the advantages and disadvantages of using TDD. Let’s go!

PREFACE

Test-Driven Development known as TDD. TDD simply is development driven by test. We must write the test first and then the working code. TDD is an iterative, incremental way to add value to your code. At the same time, you gain more confidence that the code is viable and working with each new iteration all the way into production. To understand TTD, you have to understand the main cycle of it. See the TDD cycle below:

TDD Cycle

We can conclude from the picture above, The fundamental keyword is a repetition. For simplicity, it can be written like this:

Fail — Pass — Refactor — Repeat

Now, after you understand the concept, let’s thinking about how to do it. Before doing TDD step by step, I suggest you combine it with this approach called ATDD (Acceptance TDD). This is a testing approach derived from the Test-Driven Development (TDD) methodology, which is quite similar to Behavior-Driven Development (BDD). This approach defines various ways to develop a feature based on its behavior. In most cases, the Given-When-Then approach (Simple English) is used for writing test cases.

But, wait a minute! On the beginning, you are informing us about TDD, now, you mention about ATDD, and now there is a new word called BDD. What is the difference between the three of them?? Haha, don’t worry. There is a good explanation about those differences between them. Check this link. Now, is your curiosity already fulfilled? I am sure you have understood this 😀.

Now, let me tell you the six steps to do it:

  1. Collect all requirements as detailed as possible.
  2. Before writing the code, write the test first. Make sure every test case that you’ve already gathered at point 1 is included in this test for the expected input and output.
  3. Run the test, and make sure the test is failing.
  4. Type the working code as minimum as possible.
  5. Feeling that your working code is a little mess? Refactor the code and do some cleaning. As long as the test is still passed, it means there is no problem with the refactored code.
  6. Repeat the process from points 2–5 for other functions.

The Key to Success

As defined on point 1, you have to gather all the requirements as detail as possible. This part is required to listing all the best / worst-case scenarios. This can be done with discussion when we are at a meeting, sprint planning, etc.

Code Implementation (Example)

Now, you have understood a bit of what is TDD, the primary cycle of it, and how to do it in six steps. In this section, as I mentioned earlier, we will jump into the real case with the example working code. I am using Node.js as our programming language and mocha (Sinon, chai, etc.) as part of our unit testing.

New Story:

One day when the meeting is in progress, your product owner requesting a new feature where it should return all campaigns based on the brand id. And, this task will be assigned to you. You have to make sure everything is correct & not cause any unwanted bugs. Therefore, to make it successful, you have decided to gather all the requirements before jump into the code (Step 1). After collecting all the requirements, let’s start with the Input & Output Validation.

Validate the Input :

  1. Is it requiring brand_id?
    - It’s required.
  2. What happens if it’s sending invalid brand_id?
    - It should return an error message and tell the user the brand id is invalid
  3. Is it accepting one single brand_id/multiple brand_id(s)?
    - It’s accepting single brand id only
  4. Is it needed to validate certain roles to use this feature?
    - Not for now. All roles can access this feature

Validate the Output :

  1. Define error messages for points 1 and 2. But, because we are using Joi validation, we don’t need to think much further about the error messages.
  2. Define the API contract (key-value), which will be used for supporting the main apps. In this case, the JSON response will be like this :
{
data: {
name: 'Noddle Festival',
start_at: '01-08-2020',
end_at: '01-10-2020',
products: [
{
id: 'abcdefg123456',
value: 'Chicken Noddle'
}
]
}
}

Based on the gathered requirement, let’s make a simple scenario using the Given-When-Then approach:

Scenario: Input validation not passed
Given:
The requested params of invalid brand_id / null brand id (empty)
When:
The requested params have been sent
Then:
It should return an error message
Scenario: Input validation passed
Given:
The requested params of valid brand_id
When:
The requested params have been sent
Then:
It should return a list of all campaigns

Then, it’s time to write those scenarios into your unit testing code.

IMPORTANT

Before you write your first test case, you have to :
-
Prepare all required testing environment see code line 3–13
- Write our base scenario from the Given-When-Then approach → see code line 15–33

After that, create a file inside the controller like the code below. For now, we only return `true` (it can be changed to false, 1, etc. It’s up to you to decide).

Type “npm run test” in the terminal to make sure it’s running without any error.

And… Voila! Your tests run successfully. After all unit tests are passed, let’s focus on to our next test case in `Joi validation failed` section.

Once again, type “npm run test”.

We can see at the screenshot below, those tests are FAILED because we haven’t define validation yet.

Now, let’s make all those tests to PASSED again with this additional code below.

Yap! It’s all PASSED!

But, something went wrong! Our `Joi validation passed` section is FAILED 😮 ! It’s expected because we don’t send any brand_id on that test scenario 😕.

Let’s fix the test case on `Joi validation passed`. In this part, we’ve already known that it needs to query the required data to get the list of campaigns. On the first step, we have to create the mocked modules.

Then, include this file into campaign.mocha.js. In this step, we can refactor the unit test code like removing const campaignController = require(‘../campaign’) and replace it by using this mockedModules, and any other things that you think the unit test code is “a little messy”.

Once again, type in the terminal “npm run test”.

Without no doubt, we can see at the screenshot above, the results test is FAILED because we haven’t load MongoDB for Campaign table query yet. To do so, load MongoDB for Campaign table in our campaign.js.

Yay! Our scenario tests have been PASSED successfully. 😃

Now, if you think that you have to refactor/readjust the code because of :

  1. Better algorithm
  2. Code readability
  3. New requirement regarding this feature, or any other things

Just do it! If it’s become failed again, repeat steps 2–5 on six steps section until you feel that there are no changes needed.

For Your Information (FYI)
- console.log / debugger
is Your Best Partner
-
Make your method easy to test.

Advantage & Disadvantage

After a fun and tiring session, I am pretty sure you already know what are you feeling when doing that. Are you feeling frustrated because of the need for spending more time? Or it gives you more productivity? To summary it, here are some of the advantages to be gained from compressing this TDD:

  • Helps reduce the amount of time required for rework.
  • Helps explore bugs or errors very quickly.
  • Encourages the development of cleaner and better designs.
  • Gives the programmer confidence to change the “massive” architecture of an application smoothly.
  • Results in the creation of extensive code that is flexible and easy to maintain.

Here are some of the disadvantages that will be obtained from compressing this TTD :

  • Spending more time writing a test.
  • Tests need to be maintained when requirements change.
  • All the members of a team need to do it. Unless everyone on the team correctly maintains their tests, the whole system can quickly degrade.

IMPORTANT

Beware of the Time Schedule!
The time required to do this TDD :
- On the Developer Perspective: It takes 2X than usual!
- On Project Team Perspective: Slow Development

So, that’s it. Thank you for reading! I hope this gives you an idea of about the basic concept of TDD, and how to implement it with any programming languages.

--

--

Eka Pramudita D

Someone who consciously entered the IT world. Loves drawing, eating, watching football or documentaries about anything else!