DEV Community

Cover image for The how and why on TDD
Adán Carrasco
Adán Carrasco

Posted on

The how and why on TDD

One of the crucial aspects of Software Development is the ability that our software is self tested, there are several layers to make this happen.

In today's post we are going to talk about Test Driven Development (TDD), specifically in Unit Testing.

Personally, I have been reading and even taking some courses about TDD, but I have never had the chance to apply it in real life scenarios, mostly because the projects that I have joined so far have already started some development (it's not a valid excuse).

A couple of months ago I heard of a position in a company and one of the pre-requisites was to know and have applied TDD, my CV was not even considered as I didn't have the practical experience of it, even though I knew the concept and (theoretically) I know how it works. Thanks to that I decided to give it a try and start applying some TDD in the project that I'm currently working on, even though it's been already started.

These are the steps on how I did it and manage to start doing it:

How to start?

TDD, as the acronym says: Test Driven Development, which means: "first start writing your tests then your code". Easy to read, not so easy to apply, is it? Well... It's indeed really easy, why? If you start by defining what your feature should do it's only matter of copy-pasting the feature description/acceptance criteria and from there you have your tests already set.

Let's consider the following example:
Feature requirement

From the feature description shown above, let's take the first requirement for this post's example:

The API will return relevant data for all the blocks as:

blocks/all

This is already giving us our first test to be written, the API should have a controller with the action to return all the blocks, it's also telling us how the response should be. Let's see how the test would look like:

it('returns all blocks /blocks', async () => {
    const mockResponse = [
      {
        height: 672498,
        hash:
          '00000000000000000007b619218d276894e8768876fd42f81131a98dc000da77',
        time: 1614472371,
      },
      {
        height: 672499,
        hash:
          '0000000000000000000c428a229a3c23d084cc800002faaff801a5c34edcd837',
        time: 1614473696,
      },
    ];
    jest.spyOn(service, 'findAll').mockImplementation(
      () =>
        new Promise((resolve) => {
          resolve(mockResponse);
        }),
    );
    const blocks = await controller.findAll();
    expect(JSON.stringify(blocks)).toBe(JSON.stringify(mockResponse));
  });
Enter fullscreen mode Exit fullscreen mode

From the Acceptance criteria we know that it will return as response an array of objects containing the properties: height, hash, and time. (Don't be so strict with yourself by wanting the body of the test to be full defined, it's ok if you come and update it, with the time and practice it will be simpler).

Now that we have our test, let's go to the implementation of our controller's action:

  // The controller
  @Get()
  findAll() {
    return this.blocksService.findAll();
  }

  // The service
  findAll() {
    return this.httpService
      .get('https://blockchain.info/blocks?format=json')
      .pipe(map((response) => response.data))
      .toPromise();
  }
Enter fullscreen mode Exit fullscreen mode

That's it. Now we have our Test, testing that our API is returning the expected result and the implementation of our controller returning what needs to be returned.

What are the benefits and key aspects?

First, we need to make sure our feature description is clear enough, so we can convert the requirements into Tests.

Second, once we have the tests we can start with the implementation and at the end of development we will have our code self tested. It's also good because we will be writing our code in the sense of accomplishing the acceptance criteria since the beginning, no patch, no violation of anything. As long as we have the requirement well defined our test and code will also be.

We need to make sure our feature description is clear enough

You might be thinking, I've never done a test in my life, this is going to slow me down; indeed, when starting it's going to be complicated, but as soon as you start you will have more material on how to better define your tests and make a faster progress, where you are securing your functionality to work and guarantee that there will not be any regressions.

Remember, practice makes perfect.

I hope you have enjoyed the article.

Thanks for reading!

Top comments (0)