Test pyramid

There are different kinds of tests, each one meant for a different purpose. Before describing the most common ones, let's clarify in advance a couple of terms. For service we mean a bunch of code deployed as a whole. For external dependencies we mean anything that is reached over the network, like a database, another team REST endpoint, a queue, third party apis, etc.

  • Acceptance tests
    Check that a feature behaves as expected across all service layers (e.g. back end and front end) or even across different services. External dependencies are replaced through libraries like LocalStack or Wiremock. In a web application for instance, acceptance tests are defined with tools like Selenium or Cypress.
  • Functional tests
    Check that a feature behaves as expected considering a single layer of a service. For example, if a service have both back end and front end, there will be distinct functional tests for the back end and front end. External dependencies are replaced either by libraries like LocalStack or by test doubles. Functional tests are also called component tests.
  • Integration tests
    Check that a service integrates correctly with external dependencies. External dependencies are replaced by libraries like LocalStack or Wiremock. If test doubles are implemented in code without accessing the network then it is a unit test.
  • Unit tests
    Check that code inside one class behaves as expected. Unit tests are most valuable when testing business logic: if a class is just a delegator or just coordinates other classes, do not use unit tests as functional tests already provide coverage. If the class under test uses other classes whose construction is cumbersome, those can be replaced with test doubles

The above list is ordered by how much time a test takes to execute, from the slowest (acceptance) to the fastest (unit). For this reason, it is recommended to have a pyramid of tests: a handful of acceptance tests, some functional and integration tests and many unit tests. In particular:

  • Acceptance tests
    Only for default uses of a feature
  • Functional tests
    For both default and exceptional-erroneous uses of a feature
  • Integration tests
    For both default and exceptional-erroneous integrations with external dependencies
  • Unit tests
    For both default and exceptional-erroneous usage of a single class

Recommended reads

Teach me back

I really appreciate any feedback about the book and my current understanding of software design.