BLOG POST ·
10
min read ·
November 22, 2021

TDD for beginners - Part 1

As the name suggests, Test-Driven Development (TDD) is the process of writing and running tests before writing code. This article provides an introduction to TDD and explains how the process can make writing tests much more straightforward.

Let’s talk about tests. Tests are the documentation for the code, they explain the behaviour of the code, and they prevent changes to the code from breaking the expected behaviour. Without test coverage, bugs make their way into production, the effort for fixing bugs becomes bigger than the effort for writing new code, and new functions become annoying to implement.

How does TDD add value to your regular test process? 

Firstly, because the code is designed with testing in mind, the developer already knows what functionality the code should provide. This prevents developers from writing code which is hard to test, or too complicated, driving better code quality as a result. 

Secondly, writing a test after the implementation is more like adding a test for the current implementation, by doing that the developers are likely to ignore what functionality the test should ensure; TDD can ensure that the implementation is just enough to meet the requirements, no less or no more.

TDD Cycle

TDD cycle


TDD takes an iterative approach to writing software. A test is added as soon as a requirement is clear. This test intends to fail at this point as the implementation code is yet to be written. The next step is to write the code to make the test pass quickly. After the test passes, we check the code and refactor it to make the code better. Don’t forget to rerun the test after refactoring to ensure that it passes. Once complete, we pick up the next requirement and follow the same process. TDD is the art of changing the colour of tests from red (failed) to green (passed).

TDD Structure

TDD always starts with a test. We follow the “Arrange-Act-Assert”(AAA) pattern to add a test. The process for writing a unit test is split up into three phases:

  1. Arrange
    Prepare inputs or conditions for different scenarios. In TDD, a scenario is tested with a variety of conditions to check that the code works as expected.
  2. Act
    Make the actual call to the method
  3. Assert
    Check if the tests in the method ran as expected and returned the correct output.

Tip: Keep some space between each section when writing the code to allow the reader to better understand which method is being tested. This becomes the custom in teams that are accustomed to writing unit tests instead of comments. TDD is the art of changing the colour of tests from red (failed) to green (passed).

The test will look like this:

TDD


Learn how to TDD

Here we will use an example to demonstrate the process of TDD. We are going to write a Greeter program, it will return “Hello World!”, when we pass in some names (John, Lisa, Ravi) to it, it will return “Hello John, Lisa, Ravi!”. The code for demonstration is written in C#, and the test framework is XUnit, but I think it should be easy to understand if you use other programming languages like Java, Python, Kotlin, etc.


Tasking

Normally the TDD cycle should be short enough so that we can get quick feedback when some requirements are satisfied. The test is the acceptance criteria of the requirement, and we should not put multiple scenarios into one test. Tasking is the first job once the requirements are received, we need to split the whole requirements into several smaller tasks, and then tackle them one by one.


We can split the above requirement into the following tasks:

  1. Input with nothing, output: “Hello, World”
  2. Input a name “John”, output : “Hello, John”
  3. Input: a list of names (John, Lisa, Ravi), output: “Hello John, Lisa, Ravi!” 

 

Let’s begin with the first requirement.

Requirement 1: Make the Greeter return Hello World

Follow the AAA test structure, we will have the first test looks like:

TDD

If we don’t have the Greeter class yet, the compiler will complain, so we need to write just enough code to make the compilation successful.

TDD


Now if we run the test, the test should fail as expected, as we haven't written the implementation yet. Let’s make the test pass by changing the return string to “Hello World!”.

TDD

The next thing is to see if we need to refactor the code to make it more readable and maintainable. Apparently there is not much we can do for this one line code, let’s continue with the next requirement.

Requirement 2: Greeting with one name

In this part, we will pass the Greet method a name, and want to see the greeting message accordingly. We need to add another test for this.

TDD


‍In order to make the compilation successful, we need to add a parameter to the Greet method. Then we need to run the two tests together. This is very important, as we need to make sure our change did not break the previous functionality. We should see the previous test passed, but the new one failed. Then we update the code to make it pass.

TDD


Requirement 3: Greeting with multiple names

In this part, we need to handle multiple names. Follow the same process, we will have a new test looks like:

TDD

Also we need to update the Greet method to allow passing multiple names. Then after we run all 3 tests, we will get one failed test. Let’s make it pass quickly:

TDD


Requirement 4: Greeting with multiple names and correct grammar

After we finished all the requirements, the product owner found that there is a grammar error when greeting multiple names. The output should be corrected as "Hello John, Lisa, and Ravi!".

This is a change for the existing requirement, so we need to update one of the existing tests to reflect the new requirement.

TDD


Without surprise, the test will fail. Then let’s update the code to make the test green again:

TDD

Now we have fully implemented a Greeter with all requirements satisfied. Please do remember to check if we can do some refactoring to make the code cleaner.

There is not much to do with the Greeter class, but when we look at the code, we can see that we have 3 very similar tests, the only difference is the input and output. Test framework like NUnit provides us a neat way to organise this kind of tests by using TestCase attributes. The updated test will look like as below:

TDD


In this blog, we talked about the basic things of TDD such as TDD cycle, test structure, tasking and how to TDD step by step. Sometimes there might be code which cannot be run within the context of a test and we might not have control over how it will be implemented. We will handle such cases in an upcoming blog post on TDD for beginners - Part 2.

Related CONTENT

Stay in the loop with upcoming posts

Sign up to our newsletter and receive a monthly update of new blog posts.

Tick (success) icon
Thanks for your message,
we will be in touch soon!
Oops! Something went wrong. Please try again later.