Over 2,000 mentors available, including leaders at Amazon, Airbnb, Netflix, and more. Check it out
Published

TDD is not all about writing tests first

This blog describes how TDD or Test driven development is more than just writing tests first.
Hakim Hanif

Senior Software Engineer, Amazon

What is TDD ?

TDD’s mantra:

Image

Test-Driven Development is a software development methodology where tests are written before the actual code. The process follows a simple cycle known as Red-Green-Refactor:

  1. Red: Write a test for a new feature or functionality. At this point, the test will fail because the feature hasn't been implemented yet.
  2. Green: Write the minimum amount of code required to make the test pass. The focus here is on implementing just enough to satisfy the test.
  3. Refactor: Improve the code while ensuring that all tests still pass. This step involves cleaning up the code, optimizing, and removing any redundancies.

By adhering to this cycle, developers ensure that their code is always tested and that new features do not break existing functionality.

In words:

  • Add a failing test
  • Write code to pass the test
  • Refactor

Is it all about writing tests first?

If you want to get a firm grip on this awesome methodology, read this book by Kent Beck. However, TDD is not just about writing your tests first or to increase your code’s test coverage. These are just the add-ons that you get for free when you use TDD. The mantra behind using TDD is to improve your design, to think from a consumer point of view who is going to use your components or APIs. These consumers can be anyone, it could be you, your team mates, other teams or general public.

Better Design through TDD

Then, you may ask how can writing tests first allows you to better design your interfaces? Lets think about it in this way. Why we write software? One possible answer is to create some applications that does few useful things that wouldn’t have been possible or too tedious to do manually. Its creates something with some intent that someone can use it. So two things that pop out: intent and use. I think that TDD allows you to get these two things right, the intent and use, while creating software. Again you may ask, still how writing a test helps you to get these two things right? Good question again.

When you are writing a test even without the code itself, the most important thing that comes to your mind is how am I going to use this component in my system or application. This makes you think about the interfaces, the parameters, the exceptions you want this method to throw. And there you design your API.

And then the other most important thing you think about what is the intent or the purpose of the code that you are about to write. What you want this piece of code to do? That is what makes you to write your assert statements.

The Transition to TDD

Yes, it could be very difficult to digest this test-first approach initially if you haven’t been practicing such methodology but once you do you will see the results and it value. You will be amazed to see how easy it becomes to design your components if they are easy to test. I have been using TDD for more than 5 years now and I can definitely see the difference in the way I think and develop software. Its weird but TDD makes you to not to hate writing unit tests since they are the not burdensome-after-thoughts anymore.

Free Benefits of TDD

Then, there are so many other good things that come for free when you write your tests first. It makes you confident about refactoring existing code. I worked on a system at Amazon that serves million of customers and allow them to play with their apps with almost zero downtime. How can you imagine refactoring such a service where a simple mistake can either lead to doing something unintended that breaks millions’ of users Appstore experience or can breaks a functionality (or Appstore client) in its entirety? We want our tests to fail first in such cases.

Then there is code coverage. You won’t believe the results of running a code coverage tool on a codebase that is written using TDD approach. It does not give you a single opportunity to make the coverage better by looking at the results and then ‘hack’ the tests to increase the coverage since you already get an amazing high code coverage for free!

TDD in Practice: Step-by-Step

To better understand how to implement TDD, let’s walk through a simple example.

Step 1: Write a Test

Start by writing a test for a new feature or functionality. The test should be simple and focused on one aspect of the feature. For example, if you're adding a function to add two numbers, your test might look like this:

def test_addition(): 
  assert add(1, 2) == 3

At this stage, the test will fail because the add function doesn't exist yet.

Step 2: Write the Minimum Code to Pass the Test

Next, write just enough code to make the test pass. The goal here is not to write perfect code but to get the test to pass.

def add(a, b): 
  return a + b

Step 3: Refactor

Once the test passes, refactor the code to improve its structure without changing its behavior. Ensure that all tests still pass after refactoring.

# Refactored code (if necessary) 
def add(a, b): 
  return a + b

Step 4: Repeat

Continue the Red-Green-Refactor cycle for each new feature or piece of functionality. Over time, you'll build up a comprehensive suite of tests that cover the entire codebase.

Best Practices for TDD

  • Write Small Functional Tests: Focus on testing small units of functionality. This makes tests easier to write, understand, and maintain.
  • Keep Tests Independent: Tests should not depend on each other. Each test should set up its own context and clean up after itself.
  • Use Meaningful Test Names: Name your tests clearly to indicate what functionality they are verifying.
  • Mock External Dependencies: Use mock objects to isolate the code under test from external systems like databases and web services.
  • Automate Testing: Integrate automated testing into your continuous integration (CI) pipeline to ensure that tests are run regularly.

Test-Driven Development is a powerful methodology that can transform your development process. By writing tests first, you ensure that your code is reliable, maintainable, and well-documented. While it may take some time to get used to, the benefits of TDD far outweigh the initial effort. Start small, be consistent, and soon you'll see the positive impact of TDD on your projects.

Give it a try, its worth it.

Find an expert mentor

Get the career advice you need to succeed. Find a mentor who can help you with your career goals, on the leading mentorship marketplace.