DEV Community

Yonatan Korem
Yonatan Korem

Posted on • Updated on

What Test Driven Development is really about

When I write a post, I tend to start with some definitions. What is this framework? What is the meaning of the term ?

This time, we are going to mix it up. Take a moment and think about the meaning of Test Driven Development or TDD for short? What does it mean to work this way, and why would you do it?

This is where you take a moment...

The answers I usually hear

"To work TDD, you write all your tests, and then implement the code to make them pass"

"TDD is about writing as many tests as you can to make sure you have 100% code coverage"

"TDD means you don't have a design"

"TDD is only used with low level unit tests"

The answers I hear are not wrong, but in my mind, are not accurate.
TDD is not about code coverage, though you'll probably have a high coverage with it.
TDD is not about having as many tests as you can, though the ones you'll end up with will probably cover a lot of edge cases.
TDD is not about having no design at all, just not having it up front. But you'll have one in the end.

I think that TDD is not even about tests.

TDD is about being like water.

You've probably encountered Bruce Lee's famous quote:

"Be like water, my friend"

By working with TDD, you allow yourself the freedom of not being trapped in a pre-determined design that might and might not be the right now. You shape the vessel that you need your software to fill, and fill it a little. Shape it some more, fill it some more. Your code adapts to an increasingly specific vessel.

TDD is about thinking like an artist

A sculpture doesn't carve the wood to the exact shape of the image they wish to create. They carve enough to get a rough form that looks right, and then carve some more. Slowly but inevitably they will get to the point where the carving is about the finest of details.

TDD is about knowing you are on the right path

When you work in the TDD flow, you are always adjusting, but rarely going backwards. Each test paves just a little more of the road. You rarely go backwards because you might take a wrong turn, but you will know you went backwards right away. Allowing you to fix it, go back, and try a different direction.

Enough philosophy for now

TDD is simple:

  1. Write the simplest test that should break. You know what it is, because you know what the implementation is lacking.
  2. then you make it work, without braking any other existing test. It doesn't have to be the best solution. Most of the time, the most straightforward approach is best.
  3. Clean up your (tiny) mess. You can't create a giant mess in 5 minutes. It's impossible. Cleaning up a tiny mess will take a tiny bit of time.
  4. Go back to step 1 until you can't think of any more tests.

A while ago, I watched an online lecture from uncle Bob. It wasn't even about TDD, but the topic found its way there. In that lecture, somewhat off-handedly, uncle Bob said:
"You avoid writing the tests that you know you'll need in the end. Instead, you write a test which proves that what you thought was wrong with your code, is real."

That sentence helped me understand why TDD was difficult for me to do at first. I would pick the tests that were the end goal and found myself not having that short iterations, where I could feel the code evolve. I would just write the tests that proved what I needed and then I would write the code I knew I wanted to write.
I was not like water.

Emergent Design

I once did a programming Kata that asked you to write an online API for a persistent stack. I decided to attempt it with TDD.
I avoided this process: I'll use a MongoDB for the persistency, and I'll do the backend with ExpressJS, and so on...
Instead, TDD meant that I first need the stack itself. Once that was made, I knew I could put that library anywhere and it was well tested. Then I built just the API and ran it locally. Now I know the API works. It returns garbage, but it works.
Put the stack library in the logic of the API, and I got a HTTP stack going.
The stack just used in-memory storage, so as long as the server was running, it worked.
I wrote a persistency interface that just wrote to a JSON file. That worked well, so I just put that layer into the stack, but it broke the tests because the tests did not mock it.
"Oh, right!" I realized that I should inject that layer into the stack so it just uses it like an array.

I didn't have to spend time designing up-front too much.
I didn't spend time on large refactoring sessions.

There are endless columns, write-ups, and articles about doing "enough design", and not solving problems you don't have. In its essence.
Same goes for tests, clean code, clean architecture, and so forth.

TDD is about discipline

It is a discipline you employ that helps you. It helps you create software that is clean, testable and tested, flexible, and long lasting.

Top comments (0)