‘Ice Cream Cones are bad’

At the moment a lot of code in your company has acceptance tests only. Perhaps automated acceptance tests, perhaps lots of manual tests. This is because it is hard to write code that is easy to test, and most of the code has not been written that way

‘Pyramids are good’

This policy is opinionated and demands that the code has a lot of Unit Tests. Unit tests are regarded as valuable because they force the code to be written in a style that is easy to be unit tested, which results in (generally) higher quality code. Generally these tests run quicker, are

Generally speaking the lower in the pyramid that a test is placed, the easier it is to write, check, debug and fix when it fails.

How do we invert the pyramid

The following are just examples. Each test that you write: it is important to work out how you push it down the pyramid. If it seems to hard to write it as anything other than an acceptance test: one that requires working other machines… then consult your team lead or other colleagues for advice, or seek training.

Acceptable Acceptance Tests

The only tests we should be running in the acceptance environment are Exploratory tests, Smoke tests and perhaps Performance Tests if the project decides this is the best place to do them. We should not be running and functional checks that are checking the behavior of the api or website

Change Acceptance Tests to System Tests

Any test that we currently run in the acceptance environment we should work out if we can run without other systems

Acceptable System tests

We should not be checking behavior at this level.

Changing System tests to Interaction Tests

Most of our ‘complicated tests’ involving more than one piece of software should be interaction tests and no higher.

Acceptable Interaction Tests

This should not duplicate the code in the method they call. They should mostly use mocks and simply assert that the other methods were called. The main purpose of these tests is to check the ‘wiring’. i.e. that the correct values are taken from one place and psased to another. If you use strong typing and things like ‘tiny types’ many of these tests become almost redundant as the type system does the same work as the test

Changing interaction tests to Unit Tests

If your interaction test is testing behavior then it should be re-written. Unit tests are the main place that we test behavior. Ideally your methods/functions/procedures are either simple pieces of code that only interact with primitives or vaue objects, or they are only about wiring such things together.

Unit tests

Most of our tests should be unit tests. A unit test is a test on a piece of code that only interacts with primitives and value objects. The easiest code to unit test is a pure function: this is a piece of code that uses values from the inputs and produces an output without any side effects.

Links

Counter arguments to inverting the pyramid

In my view the counter arguments are flawed because they miss the essence of unit tests and the rebuttal of them is that ‘unit tests are not about testing’. They are a design technique. They apply pressure to the code and that forces the code to adhere to good coding practices. This is captured in the link above