In the practice of modern TDD two main schools of thoughts among TDD, gurus have emerged.
Let’s start by comparing the differing characteristics of each practice and present the key proponents of each approach.
As a first oversimplification, we can broadly describe “classical TDD” as “bottom-up” and “London mockist” as top-down, as the name suggests mockist
school relies on mock objects (actually doubles but who cares?) to test interactions on the unit level. Whereas classical school would almost never rely on mocks but might use some form of fake at the integration level if needed to simplify or speed up testing (eg. changing API/ slow database). Actually, a unit is any cohesive responsibility or service and in the “classicist” view you are encouraged to view a service you’re integrating with as a unit outside your control.
In order to clear up some of the confusion around the different TDD schools, let’s examine the origin of the terms. The reason why the classical school is sometimes called Chicago is that Kent Beck, author of “Test Driven Development: By Example” was based in Chicago and the practice naturally spread there. The authoritative book for the London school is “Growing Object-Oriented Software, Guided by Tests” by Steve Freeman and Nat Pryce. It really doesn’t help that they have similar looking covers and both mention “Kent Beck signature series” the book series. You will find a lot of evangelism and training around TDD that promotes one idea or the other so don’t get bogged down with terminology and listen to “dos and don’ts” unless they make perfect sense to you.
The central schism between the two philosophies is the key focus of each. For the classical school, it is the unit: tests will be state-based tests of a class or unit. these tests will tend to be algorithmic, functional (testing functionality), state-based tests. When you hear test in this context, it is valid to imagine an engineer testing if his bicycle design will support a certain load or if its brakes will work.
On the other hand, you will find that, curiously, the proponents of the London school say that TDD is not about “testing” in the previously mentioned traditional engineering meaning. Instead, TDD would be a lot more about driving the system and architectural design and hence a design tool for the behavior and communication of those classes.
This key difference shows the biggest strengths and weaknesses respectively, in the “classical school” it easy to get bogged out in one class or unit in the classical school and miss an important detail in integration and have your classes not work well together. This might mean overall having a bad architectural design or having to start again more. This happens mostly when developers do not refactor in the red-green-REFACTOR phase. However in London school, since the focus is always on the behavior between units (interactions) this problem is avoided.
However solely focusing on interactions means it is easy to start over-engineering the design and lose focus on the core features. Let’s not forget that when following an evolutionary design, you are limiting yourself. Ygetou can easily be locked into a limited design weighed heavily by the early decisions you make. Another downside to the London school is that it introduces a lot of baggage as well it makes the cost of refactoring higher (even just in terms of syntax changes/effort). This is mitigated by the fact that with the London way it is easy to find opportunities for “micro-refactoring” and to be constantly doing it which is encouraged by Nat Pryce and Steve Freemam.
Of course, you can do architecture or interface refactoring at any time but this is a little harder and not always obvious in the “mockist” approach. The London approach is a layered approach and can introduce a lot of layers. Of course in any system this can be a combinatorial explosion of tests, of course, we only do interaction tests on “interesting” classes and we have to find a sensible set of tests spanning the total interactions. This still ends up being a lot of baggage and without spreading the cost of “micro-refactoring” by constantly doing it and taking the time for bigger refactoring you can actually end up with terrible design, no serious testing value except the acceptance tests and some interaction tests.
There is another key difference on the integration level. Integration tests gain a new responsibility in the “classical school” because they are your guarantee that the pieces fit well together. In my view, this is a more straightforward contract and truer to the meaning of a test. Here’s my black box, give it the interesting inputs (with a little gadget on some ends), try to think up of all the tests that are useful, (reliability, performance, security etc.). In the London school, you write some acceptance tests for your walking skeleton that represents a tranche of user-facing functionality, the minimal possible integration tests possibly because otherwise, you dilute the “purity” of the “Test-Driven” evolutionary approach to the design. To its merit, This is good to give you a measure of progress when you’re getting lost in your sea of interaction/unit tests and allow to do the minimum possible to satisfy most important constraints which is an important discipline for startups and enterprise alike.
At the end of the day what matters is the improvement in the maintainability of your software and user-value. My personal take is that if you’re exploring a new problem and you are confident of your software engineering skills the “classical” school is more natural and will produce the better external quality of software; as long as you remember to refactor carefully. The “mockist” school produces, in the hands of good developers, consistently good design, and makes mediocre developers have an acceptable design but not necessarily high reliability. It works especially well for pairing and when developers are trained to do “micro-refactoring” and interface refactoring (no just API refactoring but identifying abstractions in the interfaces). Which breaks the beaten path of your initial evolutionary path and makes your design more interesting.
To me, and I hope I am not the only one here, it is still a significant handicap to the ways of thinking and working with a typical software engineer. The classical school tries less to intrude on my habits as a software developer but instead encourages more reliable software. It offers fewer opinions about system design so your skills in design can show, which of course is a double-edged sword.
Test Driven Development: By Example by Kent Beck.