Microservice Testing: Unit Tests

How to build and use unit tests for distributed systems

The previous article introduced the test pyramid, and some of the basics of how to apply it to a distributed system:

Image for post
Image for post

Identify the Test Boundaries

For any test to be effective you first have to find the boundaries of that test. The goal of the test is to verify all behavior inside the “black box” of the test boundaries by manipulating the inputs to the black box, and verifying that the correct output is produced by the black box for each set of inputs.

Image for post
Image for post

Test Stubs & Mocks

In order to reproduce input conditions that come from side effects it is necessary to implement function stubs, also called mocks. This can be done using dependency injection or method swizzling. Both techniques allow a test framework to execute a tested function while ensuring that any calls out to an underlying function dependency are redirected to a different function that is a stub used for testing:

Image for post
Image for post
  • The stub can be used to return arbitrary values to simulate responses from external functions. This is useful for testing function behavior under conditions that may be hard to replicate, such as code that handles error conditions that rarely occur or are hard to reproduce.
  • The stub can be used to capture parameters that the function attempted to pass to an external function, or just record the fact that the call was made to an external function. This allows the unit test to verify expectations about what external calls the function should have attempted to make, and what parameters should have been passed.

Node.js / JavaScript

  • sinon.js (Supplies both stub and spy functionality that makes it easy to build test assertions)
  • testdouble.js (Stub generator with a focus on expressive object oriented API)
  • nock (A library that is focused on making it easy to stub out HTTP request behavior)

Python

Go

Java

Unit Test Workflow

The goal of unit tests is to give developers a way to rapidly verify the behavior of their code in between edits. Because the unit test are just making function calls to exercise logic flows while all external dependencies are stubbed out it is common to be able to execute and verify thousands of unit tests within a few seconds. The speed of these tests allows developers to run tests as part of their coding workflow, either directly integrated into their IDE, or by having a terminal to the side of their editor. By frequently rerunning the unit tests while editing code developers are able to identify potential issues before they even finish writing the code.

Image for post
Image for post

Conclusion

Unit tests are an important tool in the testing toolbox. In order to thoroughly unit test code that is part of a distributed system it is necessary to utilize a test framework that supports stubs that allow simulation of error conditions and other arbitrary responses from external dependencies. This allows unit tests to more thoroughly cover all the types of edge conditions that could be introduced by external components.

Written by

Developer Advocate for Container Services at Amazon Web Services

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store