Jake Worth

Jake Worth

A Very Short Introduction to Test-Driven-Development (TDD)

Published: September 11, 2019 • Updated: October 04, 2023 3 min read

  • testing

I’ve practiced TDD (Test-Driven-Development) a lot and feel knowledgable about when it’s useful and when it isn’t. In this post, I’d like to summarize what I’ve learned.

My Definition

It’s worth mentioning first that there are different versions of TDD. There’s outside-in or black-box TDD, red-green-refactor, BDD, ATDD, and more. They’re all in the same family, but they are distinct. Getting to caught up on terminology can quickly distract from the core concepts they share.

TDD is defined by actions. Here’s my take on classic TDD:

  1. Write an automated test that describes the nonexistent feature or bugfix.
  2. Run and watch it fail.
  3. Try to make the test pass (naively, if you’d like).
  4. Run the test again. It may fail for a different reason. Let that drive your next step.
  5. Repeat 3 & 4 until the test passes. Verify it’s passing for the right reason.
  6. Commit your code.
  7. Refactor (optional and encouraged).
  8. Add more tests.

Why Bother Using TDD?

As you can imagine, this is much harder than just writing code. So why bother? Here are my arguments.

You are guaranteed to write a test. Yes, this can be done without TDD, but TDD forces you to do it up front. In some languages this is a big deal.

You’ll get better at writing tests. More test writing, more skill and comfort writing tests.

You can guarantee that it’s possible for your test to fail. This increases the chances that the test is testing the right thing.

You might write simpler code. TDD has a habit of pre-empting clever flourishes and inexplicable extra features. If it’s not in the test, it’s less likely to be in the code (in theory).

You can use the technique to get unstuck. TDD has helped me move past a blank screen many times. You don’t have to know how something works. All you have to know is how you’d like it to work. That can often be a much easier starting point than implementation.

All this said, I don’t use TDD very often. Explaining this using the points above:

  1. I trust myself to write a test when warranted.
  2. I write them well.
  3. I know what should make them fail and refuse to let my tests ‘cheat.’
  4. I try pretty hard to write simple, boring code as a rule (TDD helped me get there).

Only the last argument— getting unstuck— is where I still occasionally use TDD as a problem-solving tool. And it’s a great tool.

Resources

Here are some resources that shaped my understanding of TDD.

  • Test-driven Development - Wikipedia as expected, a thorough overview.
  • Roman Numerals Kata - Jim Weirich (very hard to find, but probably on YouTube somewhere): Master Rubyist Jim Weirich live-codes a Ruby programming puzzle using TDD. This is where my concept of naive implementation comes from.
  • Exercism: Exercism is an open source project designed to help people get better at algorithms, and all exercises are test-driven. Deliberate practice is the key to TDD, and this is a great resource to get that practice.
  • Test Driven Development: By Example by Kent Beck. This book is credited with inventing or rediscovering TDD; I’ve never read it.

There’s writing out there saying TDD is bad, dead, or impossible to do. While I don’t agree, I recommend reading the criticism. Here are two such pieces:

Conclusion

TDD is a technique like whiteboarding: helpful in some situations. Learn how to do TDD correctly, then decide for yourself.

What are your thoughts on this? Let me know!


Join 100+ engineers who subscribe for advice, commentary, and technical deep-dives into the world of software.