2

There are two schools of thought on how unit tests should be written. The first is the Classical/Chicago school, which focuses on the isolation of unit tests, describes a unit as a class or set of classes, and allows test doubles for shared dependencies. This school lends to the Inside-Out style of development, where the core is written, then everything depending on the core is developed.

The other school of thought, is the London school. The London school focuses on the isolation of units, where a unit is a single class, and all but immutable dependencies should be mocked. This school lends to the Outside-In style of development, where you could start with something user facing, and eventually develop in towards the core of the application, mocking dependencies along the way to confirm your units behaviour.

With the London school, it's extremely easy to see where the seams are, and all immediate dependencies can be mocked to create a very clean and isolated test. An integration test would then be an integration of that class with its production dependencies. However, with the classical school, this becomes a bit unclear.

For example, let's say I have a unit under test:

public class UnitToTest {
    public UnitToTest(IDependency dependency) {}
}

As this class has an explicit dependency and I'm using the classical approach, I would already have production code available to satisfy the dependency.

public class ProductionDependency : IDependency {}

My test arrangement would look something like:

public void Something_happens()
{
    var dependency = new ProductionDependency();
    var sut = new UnitToTest(dependency);
}

When the dependency chain is this shallow, things are still pretty clear, I can put my UnitToTest under test, and focus on validating it's behaviour while using the production dependency. I can be confident in testing the behaviour of UnitToTest because ProductionDependency should have already been tested.

The classical school states that a unit is a "class or set of classes". I read this to mean that this set of classes creates a unified block which supports the behaviour under test. So now what would my tests start looking like if my dependencies have dependencies, which may have further dependencies? Should these production dependencies all be instantiated in the arrangement of my unit tests or test fixtures, or should these deeper dependencies be mocked?

In addition to this, at what point do my tests go from being unit tests to integration tests? Would an integration test then only be a test which targets an external shared dependency, like a datastore? Or would the test become an integration test if my dependencies exist in different libraries supporting the same application?

4
  • 2
    I'm actually not aware of any "schools of thought" for unit testing. I might be misinformed, but can you provide links to where you got this information? Feb 12, 2023 at 17:21
  • 2
    Is "classical school" the same as "Chicago", as used in this article? If so, it's not so much an approach to unit testing but more specific to TDD, which happens to generate unit tests.
    – Thomas Owens
    Feb 12, 2023 at 17:39
  • 1
    @ThomasOwens Yes, that's correct, I'll update the question to reflect it.
    – B-Rad
    Feb 12, 2023 at 20:13
  • 1
    @GregBurghardt The book Unit Testing Principles, Practices, and Patterns by Vladimir Khorikov is what I'm reading at the moment. oreilly.com/library/view/unit-testing-principles/9781617296277/… Is the excerpt I'm referring to when mentioning the different schools of thought.
    – B-Rad
    Feb 12, 2023 at 20:27

2 Answers 2

4

Where do unit tests stop and integration tests begin

One place to look for the answer is in the testing literature.

Integration tests: Tests that explore the interaction and consistency of successfully tested components.... The aggregate behavior of interest in integration testing is usually observed at the interface between the components. -- Boris Beizer, Black Box Testing (1995).

Integration testing is the verification of the interfaces among system parts (modules, components, and subsystems). -- Glenford Myers, Software Reliability (1976).


Where do unit tests stop and integration tests begin according to the classical school of unit testing?

What do you expect to be able to do with that answer?

The lineage of TDD has never been about "unit testing". Certainly, the principals/influencers/evangelists did use that label, until they figured out that it was a bad idea and tried to rebrand (mostly unsuccessfully).

But their interest was always in "the gap between decision and feedback" -- providing feedback to the developer during the development session; in other words tests that satisfy the constraints necessary to be viable to run between refactorings.

As far as I know, there's been no useful distinctions claimed to distinguish "integration tests that can be run between refactorings" from the other flavor of unit/developer/programmer/micro tests that we run between refactorings.

Contrast that idea with the rules proposed by Michael Feathers in 2005, intended to distinguish tests which are fast enough to run when refactoring, and tests that are NOT fast enough to run when refactoring (because they are coupled to something slow, or something unstable, or whatever).


Feathers was, at the time, of the classical/Detroit/Chicago school of TDD; so one might reasonably interpret his essay as an assertion that "integration tests" begin where the tests are slow.


Kent Beck is as classical school as you can get. Here's what he wrote in Extreme Programming Explained:

Integration immediately follows development, including integration testing.

In context: integration here refers to claiming access to the "integration machine", loading your changes, verifying that everything still works, and releasing your changes.

2

Like most things in software engineering, there isn't a fixed rule.

Instead what you have is competing ideas about how to tackle problems. Understanding that 'the problem' that the idea is trying to solve is the key to understanding the idea.

'True' unit tests, where you just test one thing and mock everything else are trying to solve the problem of. 'How can I tell what is broken', or 'How can i break this problem up into smaller bits'

'Integration' unit tests, where you mock less if at all, are trying to solve the problem of 'All my tests pass, but something is still not working'

There's no perfect academic answer to your question, you have to work out what problem you are having and try ideas that are meant to solve it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.