The Agile Echo

London vs Chicago: The two ways of TDD 🔀

We go back to talk about Test-Driven Development (TDD) today, with a little twist—we're exploring the two main different approaches of the two iconic TDD schools: London and Chicago. Each TDD school brings its unique flavor to the table, offering a different perspective on how to approach software testing and development with TDD. In this article, we'll describe the two approaches and compare them.

Cover Image for London vs Chicago: The two ways of TDD 🔀
Dan the Dev
Dan the Dev
test-driven-development

Introduction

Hello, developers! 🚀

We go back to talk about Test-Driven Development (TDD) today, with a little twist—we're exploring the two main different approaches of the two iconic TDD schools: London and Chicago.

Each TDD school brings its unique flavor to the table, offering a different perspective on how to approach software testing and development with TDD. In this article, we'll describe the two approaches and compare them.

In an imaginary travel from America to England, let’s see what London and Chicago Schools have to offer for TDD practitioners.

Let's dive in! 💻✨

A Tale of Two Cities

First of all, I want to make sure everyone knows what I’m referring to when I mention London or Chicago schools.

Chicago School TDD

When we first meet TDD, we typically first see it via the Chicago School: a.k.a. Detroit School, this school follows an Inside-Out approach; this provides a more exploratory, state-based way to solve the problem by starting from the inside of the application (from the core domain models, for example) and works out towards the APIs.

Most of the time, this is the typical approach I see when it comes to tutorials and examples online, especially those for beginners - probably just because it’s easier to start making examples on small pieces of code and then putting them together afterward.

Usually, the pros and cons of the Chicago School are considered to be:

  • Pros

    • since typically the collaborators we need already exist, It’s easier to keep tests decouples from the implementation, and the use of Test Doubles is minimized

    • It’s easier to have a higher cohesion because we start with very specific tests to more generalized ones - and this promotes high cohesion

  • Cons

    • It’s harder to respect the YAGNI principle and avoid over-engineered solutions - while TDD still reduces these risks, going from the inside to the outside can still bring to some of these situations because you look at the higher level later

Chicago School Pros and Cons

London School TDD

The London School, on the other hand, follows the opposite Outside-In approach; this provides a formal, behavior-based way to solve the problem by starting from the outside of the application (from the APIs or controllers, for example) and working its way through the lower layers.

This school is called in multiple ways (Mockist TDD, or the most famous ATDD, which stands for Acceptance Tests Driven Development) and is the approach that you typically face after you start being fluent with TDD in general.

Usually, the pros and cons of the London School are considered to be:

  • Pros

    • It’s an approach more focused on the behavior of the application and the interface used by the user - like in Contract-First API development, you start by thinking about how the client will interact with your code, and then move more on the inside later

    • CQS (Command-Query Separation principles) is easier to implement: CQS is an approach that promotes classes that have only methods that either read data or change data, which improves readability and maintainability

  • Cons

    • Higher risk of more fragile tests: starting from the outside will require using mocks to replace the collaborators we need and still don’t exist; we need to be careful and use best practices here to ensure that we don’t tight too much to the implementation, otherwise our tests will break at every change; one example? Favor manual written Fakes and Spies instead of Stub and Mocks set up with mock libraries

    • Refactoring might be harder: for the same reason, if we tighten too much our tests to the implementation, every refactoring will also require changing tests - and this is just wrong since refactoring should be done by never breaking our tests; be careful with those mocks!

London School Pros and Cons

The best of two Worlds

The two approaches both have pros and cons, and it’s important to learn them and then make use of the good side of both. But let’s make this clear: the point is not about choosing one of the two. The point is to understand them both to build an integrated adoption of these two schools.

For example, in a standard process of building a new feature that the client will use via API, I typically start the work following London Outside-In School: this way, I start focusing on the way the client will make the request, and which response he expects, to start building first from the interface he will use. Similar to the Contract-First API approach, this is a good way to ensure that the way you talk with the client is not coupled to the implementation - because the implementation still doesn’t exist. It’s a great fit, because the controller in a good architecture will typically only handle the request validation, and then talk to the Service that will implement the behavior. And if we follow CQS, creating a fake/spy for our queries/command handlers is the best way to use Test Doubles.

Chicago School is typically more useful to me when it comes to the Service because this is where the behavior will happen. First of all, sometimes the behavior is complex and the problem is hard to understand - and this is where the power of TDD comes out the most. Second of all, here we will probably have some collaborators because we might need to change the state of our application (write on a cache, file, database, or whatever - or change a runtime value) or interact with an external API.

When the need for those collaborators is clear from the start, I typically start by building those, focusing on how they specifically work (if I don’t know yet), and then putting the behavior together. I will then decide if it’s best to have a Double or use the concrete implementation in the test of the Service. When the behavior is less clear, instead, I have a hybrid approach: I typically start by implementing it all in the service in a “Chicago way” like it’s the lowest piece of the puzzle and then extract the pieces that make sense to isolate. The same decision about keeping the real concrete of the new collaborator extracted or replacing it with a Double in the test will need to happen here.

Craft your way to TDD

Finally, I want to share with you some tips from my experience about how to start practicing, and then practicing and one day even mastering, TDD - and which of the two Schools to favor.

As you can imagine, the answer is: “It depends”. The target should be to understand them both, and like I did in the example of the previous section, understand how to put them in your context and mix them to achieve the best outcomes possible.

My first tip is for beginners: at first, you should strictly follow the cycle, the law, and the best practices as they are described in TDD books, especially the original one from Kent Beck. The reason is that when we are learning something, having rules to respect is easier - and then when they become a habit and you already see some of their benefits, it’s easier to learn even more of why that works and how to become even better.

The second tip is for people that already started to become fluent with TDD at least on Katas and Side Projects: make the leap and start using TDD in real features. If you work on a legacy system, start using TDD with new code, where it doesn’t have to necessarily impact the old one. Then, move to bigger features, discover how to add tests to non-tested code properly (a bit of Refactoring is needed here), and progressively raise the bar. And then, as soon as you have the chance to work on a greenfield project, develop the system from scratch in TDD.

The third tip is for a bit more experienced developers who have worked in TDD for some years: if you never did it, consider re-reading the book from Kent Beck - I’ve done it a couple of times more after the first one, and especially the 2nd part about TDD patterns is always a discovery of new tips that you can more easily understand when you have some experience - that book is not just a tool to start, but it can also help you master TDD. In the alternative, there are some other great books that talks about TDD - check the “Go Deeper” section for my tips - and they can always give you an alternative point of view that helps your growth.

And then, finally, I have a bonus tip for skeptics: try to give TDD a chance. A real one! Trying to do TDD in real production code without practicing before - or practicing only once - it’s not a real, proper chance. I understand that some might be reluctant to the approach, but there is evidence out there proving that TDD (and some other practices such as CI, Trunk-Based, etc) produce better outcomes. My suggestion is to give yourself at least 8 hours of practicing: doesn’t matter if it’s continuous time, or splitter in 30 minutes per day (if you practice, make sure you do at least one Pomodoro) - but I think that 8 hours of practicing, while reading books or other learning content when we face struggles, is the minimum time required to be sure you started understanding something concretely. And even if you still don’t like TDD at that point, I’m sure that at least it will give you something to think about.

In the end, the key is to embrace the essence of TDD: iterative development, continuous testing, and a relentless focus on quality.

Until next time, happy coding! 🤓👩‍💻👨‍💻


It's not just writing the test, we are designing while we're writing the test.


Go Deeper 🔎

📚 Books

  • Test-Driven Development: by Example - Quite simply, test-driven development is meant to eliminate fear in application development. Kent Beck teaches programmers by example, so they can painlessly and dramatically increase the quality of their work.

  • Growing Object-Oriented Software, Guided by Tests - A practical guide to TDD that takes you beyond the basic concepts. Drawing on a decade of experience building real-world systems, two TDD pioneers show how to let tests guide your development and grow software that is coherent, reliable, and maintainable.

  • Agile Technical Practices Distilled - The book that changed my career: it explores the fundamental practices of Agile working, including test-driven development (TDD), refactoring, pair programming, and continuous integration.

  • Accelerate - Readers will discover how to measure the performance of their teams, and what capabilities they should invest in to drive higher performance. Spoiler: one of those is TDD!

📩 Newsletter issues

📄 Blog posts

🎙️ Podcasts

🖥️ Videos

Did you enjoy this post?

Express your appreciations!

Join our Telegram channel and leave a comment!Support Learn Agile Practices

Also, if you liked this post, you will likely enjoy the other free content we offer! Discover it here: