Redsauce's Software QA Blog

TDD: Writing tests first, fearlessly

Posted by Héctor Sisternas

{1570}

Do you ever feel that your software development projects become chaotic and full of bugs? Do you want to find an effective way to ensure the quality of your code from the beginning? One possible solution is the TDD (Test Driven Development) methodology, a doctrine that has revolutionised the way developers build their software.


In this post, we will explain in detail what Test Driven Development is, how it works and why you should consider implementing it in your projects.

What is Test Driven Development (TDD)?

Test Driven Development is a methodology based on writing the test first and then the code.

The main objective of TDD is to ensure that the software works correctly and meets the defined requirements.

How does TDD work?

TDD follows an iterative approach and is based on the "Red-Green-Refactor" cycle:


Red: In this stage, an automated test is written that describes the expected behaviour of code that has not yet been implemented. The test must initially fail (red), as the code does not yet exist.


Green: In this part, you implement the minimum code necessary for the test to pass (green). The goal here is to write the simplest and most straightforward code that meets the test requirements.


Refactoring: After the test passes, an improvement is made to the existing code without changing its behaviour. This involves cleaning up and reorganising the code to make it more readable, maintainable and efficient.


This cycle is repeated continuously, with the addition of new tests and refactoring of existing code, as the software is developed.


image

Benefits of Test Driven Development:

Higher code quality: By writing tests before implementing the code, a wider coverage is ensured. This leads to more reliable and less error-prone code, as every change is automatically validated by the tests.


Detecting bugs earlier: TDD allows bugs and problems to be identified in the early stages of development, making it easier to fix them before they propagate and become more difficult to fix. In addition, the execution of these tests is extremely fast.


Better software architecture: By applying the refactoring cycle regularly, code becomes cleaner, more modular and easier to understand. This facilitates maintenance, updates and collaboration with the rest of the team.


Greater confidence in the code: With TDD, every change made is validated through automated testing. This provides greater confidence in the stability and functionality of the software, allowing for safer and more frequent delivery.

How to write tests using the TDD methodology

The tests in this methodology are responsible for ensuring that the code complies with the established requirements. That is why it is of vital importance to develop solid and reliable tests. If you want to know if your tests are good, we tell you in this post.


Here are some simple steps to write effective tests using the TDD methodology:

TDD Example: Currency Converter

  1. Identify the requirement: Before you start writing a test, you should be clear about the behaviour you want to implement. This will help you focus on a specific goal and avoid unnecessary testing.

We want to validate with our test the currency conversion functionality from USD to BTC.We need a CurrencyConverter class that has a convertUSDtoBTC() method.

  1. Write an initial test: The first test you will write is one that describes the expected behaviour but has not yet been implemented. This test should fail initially, as there is no code to make it pass yet.

We will start by writing the test for the USD - BTC currency converter. This could be an example using a testing framework like JUnit in Java:

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class CurrencyConverterTest {

@Test
public void testConvertUSDtoBTC() {
CurrencyConverter converter = new CurrencyConverter();
double amountUSD = 100.0;
double expectedBTC = 0.01;
double actualBTC = converter.convertUSDtoBTC(amountUSD);
Assertions.assertEquals(expectedBTC, actualBTC, 0.0001);
}
}
  1. Run the test: Once you have written the test, you should run it to verify that it does indeed fail. This ensures that the test is correctly designed and that it captures the desired behaviour.

  2. Write the minimum amount of code: Your goal is simple, to make the test pass. The idea is to write as simple a code as possible, to get it to pass the requirement set out in the test.

Continuing with our example:

public class CurrencyConverter {

public double convertUSDtoBTC(double amountUSD) {
return amountUSD * 0.0001; // Conversión básica para pasar la prueba
}
}
  1. Run the test and verify success: Once you have implemented the code, you should run the test again. This time it should pass successfully, indicating that the code has met the requirement.

  2. Refactor the code: After this, it is time to improve the structure and design of the code without changing its behaviour. Refactoring helps to eliminate duplication, improve readability and ensure a solid foundation for future changes.

public class CurrencyConverter {
private static final double USD_TO_BTC_RATE = 0.0001;

public double convertUSDtoBTC(double amountUSD) {
return amountUSD * USD_TO_BTC_RATE;
}
}

This refactoring provides a solid base for future changes, because if we need to modify the conversion rate, we only need to update the value of USD_TO_BTC_RATE in one place.


Remember that the refactoring has not changed the existing functionality, it has simply improved the quality of the code.


Finally we must repeat the process. After getting your refactored code to pass the test, it's time to move on. Identify the next requirement or behaviour and write a new test for it. Keep iterating this cycle until you have implemented all the desired requirements.


Finally, remember these tips for your TDD tests:

  • They should be specific, cover different cases and be easy to understand.

  • Use descriptive names and make sure they are independent of each other.

  • Remember that they should be executed in a fast and automated way, which makes it easier to integrate them into the development flow.

What tools are used in TDD?

There are several tools and frameworks that can facilitate and speed up the process of writing and executing automated tests. Keep in mind that the use of one or another may depend on the programming language used.


Here are some of the most popular ones:

JUnit:

Is a unit testing framework for the Java programming language. It is widely used in its community and offers a simple syntax for writing unit tests. JUnit provides a set of annotations and assertions that allow you to define test cases and verify the expected behaviour of the code.

NUnit:

This is a tool similar to JUnit, but designed for the C# programming language. Like JUnit, it provides a set of attributes and assertions for writing unit tests. NUnit is widely used in the .NET software development community and integrates easily with development environments such as Visual Studio Code.

RSpec:

Testing framework developed for Ruby. It is based on natural language and uses a readable syntax for writing specifications and test cases. RSpec allows to describe the expected behaviour of the code in a clear and concise way, making it easy for even non-technical people to understand the tests.

PHPUnit:

This is the standard unit testing framework for PHP. It offers a wide range of functionality for writing and running unit tests in PHP projects. PHPUnit allows you to make assertions, define test cases and generate detailed test result reports.

TDD: Who, how, when and where?

When it comes to TDD, the development team is the real player in the design and execution of tests. They are the ones who take responsibility for ensuring the quality and functionality of the code they are producing.


Tests, being extremely fast to execute, can be launched at any time. We are talking about a execution time that can be as short as one second. However, to take full advantage of TDD, it is essential to set up automated test execution.


The key is to ensure that they are run right after the code is installed in an environment and, more importantly, after every commit made. This ensures that any changes or additions to the code are validated immediately. And it's free!


This is where the continuous integration server comes in. Whether it's Jenkins, Github, AzureDevops or something similar, the server must be configured with the necessary triggers so that tests run at the right times. The idea is that every time an installation is performed or a commit is made to the repository, the server will automatically run the corresponding tests.

What are you waiting for to get started with TDD?

Test Driven Development offers an effective and disciplined way to develop quality software. By writing automated tests before code is deployed, a higher level of confidence in the stability and performance of the software is ensured.


Adopting TDD will allow you to catch bugs early, improve code structure and deliver a reliable, quality product.


This and many more tips are in our free ebook. Download it now and evolve your development:


image

About us

You have reached the blog of Redsauce, a team of experts in QA and software development. Here we will talk about agile testing, automation, cybersecurity… Welcome!