Are you Detroit- or London-school?

Many people find test-driven development difficult. The learning curve can be a tough one, because there are plenty of barriers early on, and the benefits can take some time to make themselves known.

As one reader put it in an email to me earlier this week:

I’d still argue [TDD is] an advanced technique; a skill that takes a long time to hone. Inexperienced TDDers can end up going slower, write tests that don’t add much confidence and design code that is brittle and too granular.

That rang big bells for me. When I first started out, I was trying to apply TDD concepts that had been written about using heavily object-oriented examples, usually in Java.

But JavaScript allows for different styles, and we often mix and match OO approaches with functional, imperative and declarative styles in the same code base. On top of that, there is usually a lot of asynchronous I/O going on too.

Something that really helped for me was to discovering that there are two broad ‘schools’ of TDD approach.

Justin Searls has written about this distinction very clearly, so I’m going to defer to his clarity, and urge you to go and read these two descriptions:

  1. Classical or Detroit-school TDD and
  2. Mockist or London-school TDD

In summary:

  • Classical TDD focuses on testing the public API of a unit (a module, class or function in JavaScript), where London-school TDD focuses on isolating units from their dependencies, making end-to-end tests more important.
  • Classical TDD generally means working ‘bottom-up’, by first developing units that you know will later be needed by the wider system. London-school TDD works ‘top-down’, where system-wide behaviour guides the units to be developed.
  • Classical TDD doesn’t help much with guiding internal implementation, but provides a suite of regression tests with minimal use of test doubles (mocks, spies, stubs, etc). London-school TDD encourages design of units that match a handful of consistent archetypes (pure functions, collaborator objects, etc), supposedly reducing ‘blank-page syndrome’ as Justin calls it. Mock objects are used, but having lots present for one unit is a clear indicator that refactoring is necessary.

I realised that I had been mixing and matching these two styles for some time.

Sometimes I would write unit tests for a module’s public API, building a library of code I anticipated would be useful for the application we were planning to build. An example is developing a JavaScript client library to access a specific REST API.

At other times, for actual feature work, I would write an end-to-end browser-based test and work ‘top-down’ (or ‘outside-in’) to discover what units are necessary to fulfil the application behaviour.

But I would often mix the two. The result was often confusion. I would get stuck and throw away what I had done and take a different tack. If I’d had the context I do now, I would have understood why I was getting in a muddle.

For front-end web applications, I’ve found the London school approach to be the better fit. It’s not so entrenched in pattern-based development, and is less obsessed about aggressive refactoring. We’re generally writing code that’s very close to the end user, so it makes sense to start from the ‘outside’.

It’s a particularly good fit when working with JavaScript frameworks, where much of the work closely fits a small set of archetypes laid down by the framework or library (e.g. functional React components, Redux reducers).

Next time we’ll look at Discovery Testing, an iteration on London-school TDD that Justin Searls teaches, which I think fits front-end application development really well.

What confused you about TDD when you first tried it?

All the best,

– Jim

Receive emails like this in your inbox

I write about front-end engineering leadership every weekday.

Sign up now and get my Front-End Engineering Responsibilities Laundry List PDF for free.

You'll get regular emails about front-end development. Unsubscribe at any time.