A Better Way to Learn Test-Driven Development
By: Amy M Haddad
The meetup had a requirement.
About fifteen of us sat around plastic tables and chairs listening to the meetup organizer explain the problem we needed to solve. Before we broke up into small groups to solve it, he told us the catch.
We had to use Test-Driven Development (TDD), which is when you write a test first, watch it fail, and then write the code necessary to get the test to pass.
The meetup came to an end and everyone gathered together. The meetup leader made closing remarks and a request. He asked us to go around the room and share one thing that went well that evening.
I was surprised at the responses: an overwhelming majority said how much they benefited from TDD.
They had the same reaction as I did when I began using TDD for the first time: a sense of shock that this practice works—really, really well. It slowed them down. It made them think through the problem. They wrote clean, concise code.
If TDD is so great, how should you go about learning it?
The obvious answer may seem to be that you should start writing tests to solve problems. After all, it seemed to work pretty well at my Python meetup. And it did—but each group also had someone experienced with TDD to explain it and guide the process along.
Using TDD to solve problems is the end goal. But there are a few critical steps to get there.
Develop the TDD Mentality
I stumbled upon what I call the “TDD mentality” by accident. And I’m glad I did: it got me up and running with TDD quickly.
Early into my programming journey, I began solving problems on Exercism. I thought I simply had another problem-solving resource to choose from. I did; but it gave me so much more. I didn’t realize it at the time, but this was my introduction to TDD.
Each Exercism problem has a comprehensive test suite. When I began a new problem, I’d comment out all of the tests but the first one, and run it. I’d written no code at this point, so of course it failed.
I’d read the error, think about it, and work through some problem-solving tactics. Eventually I’d write some code and re-run the test.
It would fail, again.
I’d repeat these steps—read the error, think about it, apply some problem-solving tactics, write some code, and test it—until the test passed and my screen was lit in green. Then, it was time to refactor. This way of working was my introduction to the “Red, Green, Refactor” cycle that I later used with TDD.
I’d then move on to the next test and follow the same process. I worked this way until all of the tests passed. It’s a process I still use today.
I wasn’t writing tests at that point, but I got a lot of practice reading them and my errors. I also got accustomed to focusing on one test at a time and only writing enough code to get it to pass—cornerstones of TDD.
Apply the “TDD mentality” to the problems you’re already working on, long before you start writing tests of your own. The idea is to get familiar with problem-solving, and testing in general, before using TDD exclusively.
It makes learning and using TDD a lot easier, at least in my experience. Plus, you’ll find yourself getting comfortable with the TDD process while bettering your problem-solving skills.
The Big Picture
Once you’re comfortable with the problem-solving and the “TDD mentality,” it’s time to get the big picture when problem-solving with TDD.
You may be somewhat familiar with TDD already. Before diving into the weeds, get a general understanding of:
- What TDD is,
- Why it’s useful, and
- What the process looks like in action.
It’s worth building your contextual knowledge before you apply TDD to the problems you solve. It’ll make your deep dive into the practice a bit smoother, not to mention more meaningful.
thoughtbot has a great introduction to TDD. It’s in Ruby, but you can think about the process and apply the concepts in the language of your choice.
It’s very helpful to see a programmer go end to end solving a problem with TDD and hearing their thought process along the way. The instructors in this thoughtbot course do just that. You’ll come away with a good understanding of TDD and the power of this practice.
Re-solve previously solved problems using TDD, before you tackle new ones.
Why should you recycle problems?
As it relates to TDD, the aim, at least initially, is to learn the process and sharpen your skills. Until TDD becomes familiar, have a separate session for solving new problems without TDD. You don’t want to learn two things simultaneously (problem-solving and TDD).
Here’s what I mean.
While learning TDD, I spent a block of time re-solving an old problem using TDD. Another block was spent solving a new problem without TDD. That way, I got the best of both worlds: practice with TDD and practice solving new problems.
The aim of the TDD session was to get comfortable with it, which meant:
- Thinking about the problem in small chunks,
- Writing my own tests,
- Focusing on one test at a time, and consequently, one part of the problem at a time,
- Identifying edge cases,
- Being patient and letting the tests inform the code I write, and
- Refactoring a test that passes.
One of the biggest challenges people seem to face with TDD is slowing down. They’re anxious to frantically type line after line of code, thinking that the more code they write the better. Pausing to think frustrates them.
That’s also why many people, myself included, like it: TDD slows them down. It gets them to focus. It gets them to think. It gets them to write quality code.
There’s an interesting benefit when you recycle problems using TDD. The programs you wrote with TDD are far better—cleaner, more readable, and more concise—than the ones you wrote without it. The before and after difference is usually drastic.
By its nature, TDD provides feedback: you run a test and either your test passes or fails.
But how good are your tests? Did you get all of the edge cases? Are your test names clear and descriptive? To get answers to those questions, I got feedback.
Remember Exercism’s comprehensive test suite I mentioned? Well, it was really helpful when I was learning TDD. I’d re-solve an Exercism problem using TDD. Then, I’d compare my tests to Exercism’s.
Exercism wasn’t the only source I used to practice TDD, but it was a useful starting point. Once I got familiar with the process, I began writing tests for new problems.
The best part about TDD is that the results are nearly immediate.
You’ll find you’re writing higher-quality code. But something more will be gained: you’re becoming a better problem-solver.
← back to all posts