Test-Driven Development in the Wild

Matt Gardner
NYC Planning Tech
Published in
3 min readMay 14, 2018

--

Our team at Planning Labs is lean but nimble, our projects are small but impactful, and our needs differ from the usual web development paradigms: we don’t care as much about things like the “time to first paint” metric, or at all about things like “market capture”. Granted, those things still matter, but the prevailing wisdom across the web lends itself to a gap in the “best practices” appropriate for teams like ours.

Among those is practical Test-Driven Development (TDD). Five years ago when I returned to web development after a 10-year hiatus, TDD was regularly evangelized through the major frameworks at the time, especially Ruby on Rails, in which the “Red-Green-Refactor” cycle of feature development was gospel. I quickly lost faith in the approach, however: my teams valued shipping code over code quality. Our projects were small enough that full rewrites were not disastrous and were sometimes even preferred over the high cost of code sustainability, programmer-turnover-protection, and, of course, posterity. But bad code does not a happy programmer make!

I was in search of new meaning in which TDD could be balanced with the demands of small team dynamics and rapid prototyping. Alas, my faith was resurrected, briefly, by Toran Billups’ excellent presentation on the subject:

It looks so easy!

The simple magic of Toran’s performance is that he never even looks at the app in a web browser until the very end of the presentation. It inspired me to try strict TDD with my team for our next project as an exercise. For us, this would be the perfect learning opportunity.

What we learned

Many (read: all) of our web applications include complex interactive maps usually built on MapboxGL, which is hard to mock up in an automated testing environment. But, being urban planners by trade, our first instinct was to start testing the map until we realized that we were testing features that are already covered in the bindings addon or MapboxGL itself. Our first lesson? Don’t re-test the framework and addons. We want our test suite to cast a wide net while not slowing us down. Test all the delicate sinews between your domain-specific problem and the framework first before moving on. As a result, we simply moved on from the map.

Taking another look at our concept, we realized the most delicate pieces were the accordion menus and layer toggles. These components contained lots of internal state, bound different CSS class names based on conditions, rendered markup in different ways, and even rounded up counts of nested components based on state. When we discovered the most domain-specific issues in our concept, TDD went much more smoothly. Keep it simple:

Our accordion should yield content in a separate element.

That being said, TDD still took time and practice. We dedicated a whole day in a conference room to working through the awkwardness of learning the mindset. It was a lot like learning how to form an embouchure to play a brass instrument using only the mouthpiece. At the end of the day, once the constraints are removed, you find yourself performing much better by testing much smarter.

For small teams like ours, we learned we had to be strategic about what to test. We don’t have the luxury of time to TDD our way through a sprint, but we can make smart choices by testing domain-sensitive features. Our takeaways?

  1. Prioritize testing domain-specific areas of the app rather than other people’s APIs
  2. Start with testing the linkages between your domain-specific code and the framework/libraries you’re using
  3. Keep acceptance tests dead simple — move down to an integration test if you find yourself spending too much time on it
  4. Practice makes perfect — TDD is like learning an instrument.
  5. Don’t despair — some test coverage is better than none. If you catch a bug, writing your tests was time well-spent.

For further reading, I very highly recommend reading Sandi Metz’ excellent 99 Bottles, which is an exercise in practical TDD:

--

--