Max Polun's blog

The Two types of TDD

[This recent artical](http://iansommerville.com/systems-software-and- technology/giving-up-on-test-first-development/) and it’s hacker news comments Have gotten me thinking about testing and TDD, specifically how there are different reasons why people do it. I think it helps to go back and look at the two “Schools” of TDD to understand what each one was trying to get out of their tests and what the strengths of the two approaches are relative to each other (and relative to non-TDD methodologies).

First, a history lession: TDD first originated in the [Chrystler Comprehensive Compensation Project](https://en.wikipedia.org/wiki/Chrysler_Comprehensive_Com pensation_System). In fact many of the practices we now consider to be good agile practices came out of the “Extreme Programming” community that really found their footing at the C3 project. The TDD that was done by this project was focused on preventing regressions. This means the tests are fairly realistic, and only test interfaces, not internal details. This is known as “Detroit School” TDD.

After TDD became more popular, a technique for doing TDD that relied heavily on test doubles (stubs, mocks and spies) was described in the book Growing Object Oriented Software Guided by Tests. In this methodology, tests were primarily a design tool. Since the authors of the book were both from London, this is known as the “London School”.

Most people doing TDD don’t consiously follow any particular school, however the Detroit school makes intuitive sense to most people: test help prevent bugs, right? However the Detroit school is prone to the types of problems described in the original artical: You end up with lots of duplicate tests, tests need maintenance as much as code, and refactors that change the interface of components get harder and harder as there’s more tests. Early design decisions get frozen in the codebase.

The Detroit school isn’t perfect, but it does shine at building reliable components. Since the tests are relistic and redundant there’s a good chance that you’ll know if something breaks. Internal refactors are also easy to do. Only interfaces are tested and so anything that doesn’t change them is safe.

The London school avoids the problems of Detroit school: in the London school you use mocks and dependency injection to avoid having any redundancy in your tests. The goal is that if you make one change, all breakage should be isolated to that one component. This makes your tests easy to maintain, and encourages a certain type of design. However because you have to specify the behavior of your dependencies via test doubles refactors involve much more work. This is less of a problem than you’d expect. A London school practicioner would say that if you need to change your interface you should just throw your old code away and rewrite everything. London school code is usually fast to write so it’s a viable strategy. They do have end to end tests that test for regressions, but unit tests aren’t really looking for them.

So these two schools of thought lead to a lot of the confusion about TDD. If you are doing Detroit school TDD you should avoid test doubles as much as possible. Likewise if you are doing London school TDD you shouldn’t be doing any refactors, just throwing away old code. Trying to combine these schools of thought within a single component usually leads to pain.

But what about using different types of tests for different components? Most applications have code that’s tied to the implementation (things like database code, networking, logging, etc) and code tied to the problem domain (aka your application logic and/or business logic). Implementation code should be stable and reliable, which to me sounds like Detroit school TDD. The higher level logic is going to change more often, doesn’t have many dependencies between high level components, and can probably be thrown away and rewritten if there are significant changes, which sound sideal for London school TDD. Now not everything falls neatly into these two buckets so you’ll have to decide how you’re going to test those components, and you should probably chose either Detroit or London school testing for each one. I do think that this way of looking at testing is a helpful way to think about it.

So why do TDD at all? TDD does always involve more work to build and maintain your testsuite. Not having tests has well known problems: fear of changes since any change can break anything and you might not find out until a tester happens to discover it. Test-after seems better, but has the same test- maintenence issues as TDD but since you’re writing the tests after you don’t have as much confidance that they’re working. TDD is not without issues, but I still think that it’s the best way to write nontrivial software.