mobile menu icon

The class is not the unit in the London school style of TDD

Published by Manuel Rivero on 03/03/2025

Testing, TDD, Test Doubles


Introduction.

A dangerous misconception: considering that the class is the unit in unit testing.

Considering that the class is the unit in unit testing has terrible effects. If we follow this idea, we’ll use test doubles to isolate the class under test from any class that collaborates with it (its collaborators). This will produce tests that are highly coupled with implementation details, and therefore very brittle (high structure-sensitiveness)[1].

Considering the class as the unit...
Considering the class as the unit...

This brittleness will make those refactorings affecting interfaces to which our tests are coupled more expensive (they will take more time[2]), as we’d be forced to change the tests as well. This increase in cost will hinder refactoring.

Many problems when using test doubles are caused solely by this misconception. Thankfully it seems to be receding, as more developers are starting to understand that the unit in unit testing is the behavior and not the class. This understanding helps to produce less structure-sensitive tests which don’t hinder refactoring.

The class is not the unit.
The class is not the unit.

The surviving misconception: considering that the class is the unit in the London School or Mockist style of TDD.

Recently, we have noticed many videos, posts and even books [3] that affirm that the, so called, mockist style of TDD considers that the class is the unit in unit testing.

Unfortunately this other widespread misconception is making developers, either apply the mockist style of TDD completely wrong (testing every class in isolation from its collaborator), or choose not to use it altogether to avoid the class-is-the-unit problems we described before.

In the remainder of this post, we will show that the mockist style of TDD does not consider the class is the unit in unit testing.

In our discussion below, we will use the book Growing Object Oriented Software, Guided by Tests (GOOS) by Steve Freeman and Nat Pryce as the reference for the mockist style of TDD.

The class is not the unit in the mockist style of TDD.

Exhibit 1: Values & Objects.

In the section Values and Objects (chapter 2: Test-Driven Development with Objects) of the GOOS book its authors distinguish between two concepts:

When the word “object” is used in the GOOS book, they refer only to the second concept.

Don’t double values!!

As its title says, the section Don’t Mock Values (chapter 20: Listening to the Tests) recommends that we do not use test doubles to simulate values in tests.

The GOOS authors say even more:

“There’s no point in writing mocks for values (which should be immutable anyway). Just create an instance and use it.”

This recommendation in itself shows how the idea of isolating each class by using test doubles of every class that collaborates with the class under test has never been a part of the mockist style.

Likely, the confusion may have arisen because both concepts, values and objects, are implemented by the same language construct in most OO languages: the class.

Even though some authors acknowledge the distinction between values and objects, they still that the class is the unit in the mockist style of TDD[4]. However, we view this claim as a contradiction.

There are more ideas in the GOOS book that refute the false belief of considering the class is the unit in the mockist style of TDD.

Exhibit 2: Internals vs Peers.

For the GOOS book authors not all the collaborators of an object are considered “real collaborators”.

In the section Internals vs Peers (chapter 6: Object-Oriented Style), they distinguish two types of collaborators:

Internals vs Peers from Mock Roles Not Object States talk.
Internals vs Peers from Mock Roles Not Object States talk.

Sadly, this distinction between internals and peers is probably one of the less understood concepts in the GOOS book.

Recognizing peers.

The GOOS book authors consider that an object’s peers are “objects with single responsibilities”, i. e., following the single responsibility principle, that can be categorized (loosely) into three types of relationship.

Peers (real collaborators) stereotypes:

Any collaborator that is not a peer would be considered an internal.

They only consider these stereotypes as heuristics to help us think about the design, not as hard rules. Later in the book, in the section Where Do Objects Come From? (chapter 7: Achieving Object-Oriented Design), they explain other heuristics that we can apply to discover an object’s peers: ”Breaking Out”, “Budding Off” and ”Bundling Up”. We’ll talk about these techniques in a future post.

In their talk Mock Roles Not Object States, Freeman and Pryce provide an excellent explanation of the concepts of internals and peers when discussing the “The test exposes internal implementation details of my object” test smell.

Don’t use test doubles for internals!!

Identifying an object’s peers is crucial to minimize the coupling of tests to implementation details, since internal objects are not intended to be simulated in tests:

“We should [only] mock an object’s peers—its dependencies, notifications, and adjustments […]—but not its internals” (chapter 7: Achieving Object-Oriented Design).

In our tests we will only use test doubles to simulate the behaviors (roles, responsibilities) that the behavior we are testing depends on, not the classes it depends on. This is related to the recommendation “mock roles, not objects”. And those depended-on behaviors are the peers of an object.

In our opinion, internals should not be injected from the outside; instead, they should be created within the constructors of the objects they belong to.

What is the unit in the mockist style of TDD then?

We hope that, by now, we have managed to show that the class is not the unit in the mockist style of TDD. What does the mockist style of TDD TDD consider the unit then?

Just like the classic style of TDD, the mockist style of TDD also considers behavior as the unit in unit testing[5]. And, as we have seen, a behaviour might be implemented by 1 or N classes, but that is only an implementation detail.

Conclusion

The purpose of this post was to dispel a common misconception about the mockist style of TDD that states that the class is the unit in the mockist style of TDD.

In some cases, this misconception comes from not having read or not having understood the GOOS book well.

In some other cases, we think that, sadly, this idea of the class-is-the-unit is used as part of a straw men intended to criticize the mockist style of TDD by saying that, all the maintainability problems associated with the class being the unit, are produced by using the mockist style of TDD.

In any case, by dispeling this common misconception, we hope to facilitate better discussions about the mockist style of TDD trade-offs and enable its more effective application.

Acknowledgements

I’d like to thank Fran Reyes, Alfredo Casado, Emmanuel Valverde Ramos, Fran Iglesias Gómez and Marabesi Matheus for giving me feedback about several drafts of this post.

Finally, I’d also like to thank William Warby for the photo.

References.

Notes

[1] When we test classes in isolation, the resulting tests will have a high structure-sensitiveness as opposed to the ideal of tests being structure-insensitive (see Test Desiderata 2/12 Tests Should be Structure-Insensitive) .

[2] If we apply a parallel change the test will not break at any moment during the refactoring, but this is more costly than doing a refactoring when the tests are not affected.

[3] Search the internet and you will find multiple examples of the misconception we are addressing in this post.

[4] This happens in the book Unit Testing Principles, Practices, and Patterns.

[5] The authors of the GOOS book state that we should “Unit-Test Behavior, Not Methods” (chapter 5: Maintaining the Test-Driven Cycle).

Volver a posts