DEV Community

loading...
Cover image for Why I Love TDD

Why I Love TDD

shanif profile image Shani Fedida Updated on ・4 min read

In this post, I show the day to day differences between TDD (Test Driven Development) and development without tests or write tests after the feature is complete (TLD) .

TL;DR

  • TDD takes less time than non-TDD approaches because it reduces manual testing, which takes a lot of time.
  • Tests are a safety net. In TDD, we have a safety net while coding, not only after we finish the development.
  • TDD is a methodology that helps developers to focus and work in baby steps, those reducing the cognitive load.
  • TDD encourages clean code.
  • Life is easier with TDD because we start with a small amount of code, which slowly grows.

Non-TDD Approaches

Development Without Tests

In this approach, we repeatedly:

  1. Choose scenarios for testing.
  2. Write code for those scenarios.
  3. Test from outside and manually examine that the scenario works correctly by, for example, viewing the data in the UI, looking at the results of API calls, and performing DB queries.
  4. Fix the code until the scenario works as planned.

When do we check previous scenarios still work? I guess each developer checks at a different time: when finish to develop the feature, after each scenario, and even by the number of code changes.

Tests Last Development

This approach is similar to development without tests except that we write tests after we developed the feature in order to validate the feature works correctly.


TDD 101

The overall idea is to write code incrementally. We write code for a specific scenario while always validating that other scenarios still work. Also, we care about clean code, but at a separate stage.

Alt Text

For each scenario, we:

  1. Write a test that represents one scenario. We run the test, and it fails* because we didn’t write any code for it to pass(Compilation errors count as failures)
  2. Write the minimal code for the test to pass.
  3. Refactor the code.

*If the test doesn’t fail, it usually means that we wrote more than the minimal code at a previous iteration, or that the test is equivalent to an existing one.


TDD vs. Non-TDD Approaches

Tests Duration

In non-TDD approaches, we test our code manually by reproducing the same flow over and over again. Reproduction of the flow can include: application loading, login, get to the relevant screen and press a button. The feedback is slow and requires us to remember a sequence of actions.

In TDD, we run all the tests with a press of a button. A unit test usually takes a few milliseconds; an integration test usually takes a few seconds.

TDD takes less time than writing tests in the end because the outcome is the same, but while coding, we still need to test somehow, so we perform manual testing. I believe that TDD also takes less time than develop without tests because the manual tests take more time than write tests.

TDD gives us a simpler and faster way to test our code.

Tests Are a Safety Net

We tested a few scenarios, and we are now implementing the next one. We changed the code, so we should retest the existing scenarios.

In the Non-TDD approach, we test manually. Manually testing takes time, and it’s a repetitive and precise task — not something we humans excel at. In the best case, we documented the scenarios; in the worst case, they are inside our head. What are the chances we really retest all scenarios? Even if we documented the scenarios, do we really retest them all? Or do we skip some because the process is long and repetitive? (I would even say boring).

In TDD, with a press of a button, we test all existing scenarios after each code change.

Focus — Less Context Switch

Alt Text

Focus VS Cognitive Overload

TDD is a methodology that defines a work order. Each task has its time: write a test, write production code, and refactor.

In non-TDD approaches, everyone has their methodology, even if they are not aware of it.

Focusing on a specific scenario and the three stages create a process of small and defined steps — Baby steps 👶. This kind of process creates focus and lowers the cognitive load.

I noticed that for me:

  • The refactor part helps me to avoid refactoring while coding. More than once, I started to refactor while writing code and ended with messy and broken code. I had to discard the code. 😢
  • Focusing on a specific scenario helps me ignore other scenarios while implementing it, which reduces my cognitive load.

Clean Code

In non-TDD approaches, every developer decides when to refactor. From my experience, we postpone the refactor to the end of the feature. The problem is that at some point soon, we suffer from the mess we made during the development.

In TDD, there is a dedicated time for refactoring. Because it is at the end of each cycle, we work with more maintainable code while developing.

There is a better chance that working in TDD will produce a cleaner code than the non-TDD approaches, but it depends a lot on the developer.

In TDD the Code Is Written Slower

I know it sounds like a disadvantage, but…

In TDD, we start with a small amount of code, which slowly grows. At every stage in the development, we have less code: less code to read, less code to debug, less code to navigate through =>Life is easier. 😌


I hope I convinced you to try TDD. You are welcome to contact me, and I will be happy to help you adopt it.

Discussion

pic
Editor guide
Collapse
hallsamuel90 profile image
biggHealthy

A few months ago, I switched to a project that had a required code coverage metric (never wrote tests for the other projects).

At first I was writing the tests after the fact. By doing this, it was almost impossibe to test some functionality and it always felt like a chore.

Fast forward to today and I am following a tdd approach and it has drastically improved the cleanliness and quality of my code. It's also a great feeling being confident that your code 'works' by the time you complete a feature.

+1 for tdd.

Collapse
omerxx profile image
Omer Hamerman

Great piece Shani!
You’ve inspired me to go back and try TDD on projects that have been developed tests-less for ages.
Thank you

Collapse
shanif profile image
Shani Fedida Author

I am so happy to hear that! Do you mean you are going to re develop an existing project in TDD for the exercise or that you are going to try TDD on a new development?

Collapse
omerxx profile image
Omer Hamerman

I'm going to apply tests to recently added modules. There are literally zero tests in the application.. then I've committed myself to start working on the next feature in TDD. It makes so much sense

Thread Thread
shanif profile image
Shani Fedida Author

Great! If you need help will be glad to help. Contact me via Twitter.

Collapse
twtsuganya profile image
Suganya Muthukumar

Yes, even I am a big fan of TDD.. It saves a lot of time on manual testing and missing out scenarios.. It specifically comes handy when you have to follow big process steps to test a small code change.. Though initially it takes a little of your time to write your test cases, at the end it's worth the effort.

Collapse
miketalbot profile image
Mike Talbot

I do love TDD on the server because "it makes sense" to have a framework to ensure your code works. I use the excellent Wallaby.js that runs tests in the background as you type and integrates with VSCode/other IDEs to show issues and code coverage in real time - I love it.

What I don't love is the hours wasted making mocks for things in increasingly complicated systems. It starts off making sense, then eventually you end up debugging your mocks and how they are getting loaded. There are circumstances where this ends up being hard and where it isn't clear what all of the input characteristics to a unit will be - this can lead to a false sense of security. On the server side, I think my team have a good balance on this. Properly stacking the levels of deeply tested 95%+ code coverage core modules with integration driven tests higher up.

But then I consider the front-end. Sure it isn't too bad if you are building a library component, but yeah, there is more time wasted in unit testing than in working if you aren't careful. We are Cypress and integration tests all the way because so much of the FE work is about "how smooth it looks" and does it make sense as well as "did this button appear and did X happen when I clicked it".

Collapse
jonrandy profile image
Jon Randy

"This approach is similar to development without tests except that we write tests after we developed the feature in order to validate the feature works correctly."

I've been a professional developer for 26 years and have never written tests for my code - before, during, or after writing the code.

Collapse
goncalofframalho profile image
Goncalo FF Ramalho

26 years of experience, and a lot of manual testing. Just because its fine for you, doesn't mean its the better way. 😊