Unit vs Integration tests, Detroit vs London school of TDD

Julius Koronci
4 min readDec 23, 2019

This article started with an argument at work about what actually is a unit test. I had a simple JS function which imported a few internal and external methods which were not mocked in the tests. My colleague pointed out that if imports of packages or modules are not mocked it is an integration test and not a unit test. I of course argued that it doesn’t matter whether the imports are mocked or not, it is still a unit test.

Testing pyramid

So, what is a unit test, how it should look like and how much of it we should have?

Based on Wikipedia the difference is the approach or the purpose we want to achieve by the test. Based on stack overflow, well we will never know as opinions differ.

After spending a few days reading through dozens of articles I finally came to a reasonable conclusion and simplification.

Let's introduce two definitions and few examples in a human understandable language :)

Unit tests

Unit test is a test of a unit of code (function, class, file, module…) which is tested in isolation from the system in which the code runs. It can be any part of the system until we are able to test it independently from parts which may change as the system runs.

Integration tests

An integration test is a test which validates that a part of the system interacts correctly with another part of the system or the entire systems. The focus of the test is on the interactions and not the individual parts of the system.

Do we need to mock imports or we don’t? that is the question :)

Short answer is no, long answer is, it depends.

The idea here is that if we are importing a package or utility (3rd party or internal) and this “dependency”:

  • is not injected into our unit by the system,
  • it will not change as the system runs,
  • it has no side effects which may result in different behavior of our unit of code

than it is considered part of our unit of code and we don’t have to mock it.

Libraries or module imports will not change as the system runs, they will only change if we upgrade a library or change the code and recompile our system.

If we have dependencies which are injected into our unit by the system e.g. classical dependency injection(getter, setter or constructor …) than these dependencies need to be mocked otherwise our unit will depend on the running system and the test will not be able to validate that our unit works as expected. This test would be considered an integration test as it tests that our unit interacts with the system correctly.

Detroit vs London school of TDD

Since we established an agreement on unit tests, we can now talk about whether to mock imports or not? Again, there is no correct answer here and all approaches have its pros and cons.

We can narrow them down on 2 main approaches

Mock everything (London school)

Isolate the unit from everything outside of the unit and make sure you only test what is written in the unit. This strictly tests that whatever is written in the unit, works exactly as expected, your tests is completely independent, so if you upgrade a library which introduces a breaking change, your test will still work.

Mock only 3rd party dependencies (Detroit school)

Isolate your unit of code from dependencies which are not under your control. This means you should only depend on the public interface of your 3rd party dependencies and not on their internal implementation as you have no control of its internal workings.

Pros and Cons and which one to choose

Ask yourself the question what is it you want to achieve by testing?

For instance I want to make sure that my application will not break with changes introduced over time.

The correct strategy also depends on the approach to testing and test coverage. In general if you don’t have a 100% integration or e2e test coverage on your features and you go with the London approach. Your unit will work as expected but your system might break. With the Detroit approach, even though your tests are more brittle and may fail just by the fact that someone else changed a module which is not related to your unit of code and you might need to fix tests which were completely fine, at the end of the day your application will work correctly.

Everyone can pick their choice but my preference and recommendation for now is to go with the Detroit approach, the tests will be less stable but your system will be more stable.

At the end of the day we are building applications and we want to make sure that they work as expected and deliver features which make our clients happy and our clients don’t care about perfect code, perfect tests which is also why the inverse pyramid of testing is becoming so much more popular.

--

--