Why microservices need automated tests, how to balance them, and how to write good unit tests.
key idea / coordinatorservice / good pathin progress / watch outproblem / failureneutral
Why automate
FTGO tested by hand, after coding. Microservices make automated tests even more necessary.
Testing basics
Every test sets up, executes, verifies and tears down. Doubles stand in for slow or missing dependencies.
Organizing tests
The quadrant says why a test exists. The pyramid says how many to write.
Purpose
Technology-facing
Business-facing
Supports programming
Unit, integration (automated)
Component, end-to-end (automated)
Critiques the product
Performance, non-functional
Usability, exploratory (manual)
Test scope and balance
Newman splits tests by scope: what is switched on while the test runs. A wider scope gives more confidence, but costs speed and clarity. From Building Microservices, ch. 9.
A unit test calls one function and starts no services, so thousands run in under a minute. A service test starts one microservice and stubs what it calls. An end-to-end test drives everything, often through a browser.
Newman avoids the word integration test because people mean different things by it. Rule of thumb: about ten times more tests with every step down.
The microservice challenge
Services talk over the network all the time. Contracts keep both sides honest without running them together.
Service tests with stubs
A service test deploys one microservice, for example the container image from the build, and replaces every service it calls with a stub. If the test breaks, the fault is in that service. From Building Microservices.
Stub
Mock
Smart stub server
Does
Returns canned replies
Returns replies and fails the test if the call never came
A programmable fake you configure over HTTP
Cares how often called?
No
Yes
Your choice
Best use
Default for service tests
Check a side effect, e.g. a points balance is created
Fake several downstream services at once
Newman uses stubs far more than mocks in service tests, because overusing mocks makes tests brittle. Meszaros groups stubs, mocks, fakes, spies and dummies as test doubles.
mountebank: a small server you program over HTTP. You create imposters, each listening on a port with a protocol (HTTP, HTTPS, TCP or SMTP) and the replies to send. One instance can stand in for many services. It cannot fake message brokers, which is where Pact helps (next lesson).
The deployment pipeline
Each service gets its own automated path to production.
Flaky tests and developer experience
More moving parts means more ways to fail for reasons unrelated to the feature: a service is down, the network glitches, a thread races or a timeout hits. A test that sometimes fails and is simply re-run is flaky. From Building Microservices.
Why act fast? Teams drift into normalization of deviance: they get used to a suite that always fails like that, and real breakages hide in the noise. Sometimes the best fix is to change the software so it is easier to test.
As the number of services grows, running them all on one laptop hurts. Newman prefers that a developer runs only the few services they work on and stubs the rest. Moving development to the cloud lengthens the edit-and-test loop.
Writing unit tests
The base of the pyramid. Pick the style by the class's role.
Unit testing with Jest
In practice, a unit test isolates code by mocking its dependencies: swapping the real function, object or library for a fake you control. This also removes slow work such as database queries, network calls and file access. From Bootstrapping Microservices, ch. 9.
The test file sits beside the code as index.test.js. A describe block names the suite, each test runs the code and expect checks the result. The book tests that the service starts and that its /videos route reads from the database.
Command
What it does
npx jest or npm test
Run all tests once
npx jest --watch
Live reload: rerun tests when code changes
npx jest --init
Create the Jest config file
Design for testing: the book exports a startMicroservice function and only auto-runs it when the file is the entry point. Reshaping code so tests can call it is normal, and it is how test-driven development nudges design.
Tests as a gate in CI/CD
Tests only pay off when they run on their own. The pipeline runs them on every push, and production deployment waits for a green result. From Bootstrapping Microservices.
Aim for hundreds or thousands of fast unit tests, fewer integration tests and very few end-to-end tests. Slow tests get skipped by developers. The line between test types is blurry: all tests sit on a spectrum.
Test independently: if teams deploy independently they should test independently, ideally in their own environment. Research summarized in Accelerate found high performers do most testing on demand, without a shared integrated environment (Building Microservices).
Recap
Automate tests. Use doubles: stubs return values, mocks also verify calls.
Follow the pyramid: many unit tests, few end-to-end tests.
Consumer-driven contract tests check service APIs without end-to-end tests.
Unit test entities, value objects, sagas, services, controllers and handlers.
Wider test scope gives more confidence but slower, harder-to-diagnose failures. Aim for about ten times more tests per step down and avoid the snow cone.
Service tests run one service with stubbed neighbors (for example mountebank). Prefer stubs; use mocks to check side effects.
Flaky tests erode trust: quarantine, then fix or replace them. Run tests in CI so they gate every deployment.
Quick check
You replace OrderService with a double and check that OrderController called findById() once. Stub or mock?Show answer
A mock, because the test verifies the call. A double that only returned a value would be a stub. This is a solitary unit test.
A suite has 20 unit tests and 60 browser tests and takes three hours. What is the pattern called, and what is the first step?Show answer
A test snow cone (inverted pyramid). Replace slow broad tests with smaller ones, delete duplicated coverage, and make the suite fast.