🧭 Analogy
You don’t crash-test a whole car to check the seatbelt buckle. You test the buckle in isolation, verify it bolts to the right mount, confirm it meets the safety spec — then run a few full crash tests on the journeys that matter most. Serverless testing works the same way: test the parts, verify the joins, and reserve end-to-end runs for critical paths.
Why serverless testing is different
The very attributes that make serverless powerful — decoupling, event-driven communication, managed services, distribution, cloud-native execution — make the traditional “deploy the whole system and test it end-to-end” strategy counterproductive. The core thesis: if your services are decoupled in development and at runtime, do not re-couple them during testing. Difficulty comes from variable latency, acknowledgment-only async communication, opaque AWS-owned services, eventual consistency, and the hardness of local emulation.
Rather than chasing maximum coverage or zero bugs (bugs are inevitable — “everything fails all the time”), the aim is maximum confidence in minimum time.
The serverless square of balance
graph TD T["Test"] --- D["Deliver"] D --- O["Observe"] O --- R["Recover"] R --- T C["Maximum confidence,<br/>minimum time"] --> T C --> O C --> R
Keep test, deliver, observe, recover in equilibrium. “Stability is a product of speed” — so trade some pre-deployment coverage for observability and recovery. Distinguish critical paths (user present, time-sensitive, synchronous, business-critical data → test coverage + alerting) from noncritical paths (background, recoverable → alerting + fault tolerance, can ship more bugs within error budgets).
Three building blocks to test
An event-driven app has three things worth testing:
- Business logic — abstract it from the handler and unit-test it in isolation (TDD).
- Integration points — Configuration (→ infrastructure + unit tests), Permissions (→ infrastructure tests, e.g., asserting IAM via CDK
Template.hasResourceProperties), and Payloads (→ unit, contract, and static analysis). - Data contracts — verify requests/responses against agreed types via contract testing (JSON Schema with Ajv, or Pact) and static typing.
graph TD BL["Business logic"] --> UT["Unit tests (TDD)"] IP["Integration points"] --> IT["Infrastructure + unit tests"] IP --> CT["Contract tests"] DC["Data contracts"] --> CT DC --> SA["Static analysis"]
Don't test what AWS owns
“If you can’t fix it, you shouldn’t test it.” Don’t write tests that exercise AWS’s internal behavior. Test your configuration, permissions, and payloads — for example verify an EventBridge rule’s pattern with TestEventPatternCommand without invoking the underlying services. Mock any code you are not responsible for testing or fixing.
Just enough, just in time
- Just enough testing — prefer static testing (unit tests + static analysis, no deployment); use mutation testing to find useless tests; remove flaky tests cautiously.
- Just-in-time testing — limit the number of test runs; run as few times as possible, as close to production as possible — a shift-right posture tied to continuous integration (integrate to trunk at least daily).
- Environments — use as few as possible. Avoid per-PR ephemeral environments (the “antithesis of CI”); prefer instant environments isolated to two or three components.
- Post-deploy — add synthetic canaries and load test critical paths before big events.
Two supporting tools: a Definition of Done checklist (quality measure, release method, failure detection pre- and post-prod, debuggability/tracing, recovery) and FMEA (Failure Modes and Effects Analysis): probability × severity, adjusted by detection, to prioritize where tests and observability go.
Key insight
Treat a serverless system as a collection of distinct applications and apply quality controls per component. The async equivalent of an end-to-end assertion is a retried assertion with a timeout — e.g., “order placed → an orderCreated event appears on the bus.”
See also
- Event-driven serverless patterns — the integration points you test.
- Observability and cost — observe and recover, the other half of the square.
- The Well-Architected lens — operational excellence and reliability.
When to use it — and when not
✅ Reach for it when
- Testing distributed, event-driven systems built from managed services
- You want to balance pre-deploy coverage against observability and recovery
- Focusing quality effort on critical, time-sensitive, user-facing paths
⛔ Think twice when
- Trying to deploy the whole system and test it end-to-end as one unit
- Chasing maximum coverage or zero bugs (bugs are inevitable)
- Testing what AWS owns — 'if you can't fix it, you shouldn't test it'
Related topics
Compose serverless systems from named patterns — storage-first, functionless integration, gatekeeper bus, choreography vs. orchestration — over an event-driven backbone.
cld-serverlessObservability and CostOperating pay-per-use systems: infer internal state from metrics, logs, and traces, and treat the monthly bill as a direct measure of architectural efficiency.
cld-serverlessThe Well-Architected LensThe AWS Well-Architected Framework's six pillars — plus the Serverless Lens — give a shared vocabulary for reviewing trade-offs across security, cost, reliability, and sustainability.
Check your understanding
Score: 0 / 41. What is the goal of serverless testing?
Because bugs are inevitable, serverless testing trades some pre-deploy coverage for observability and recovery, focusing on critical paths.
2. What are the three building blocks of an event-driven app to test?
Business logic → unit tests; integration points (configuration, permissions, payloads) → infrastructure/unit/contract tests; data contracts → static analysis and contract tests.
3. What is the 'serverless square of balance'?
Stability is a product of speed: trade some pre-deployment coverage for stronger observability and recovery.
4. Why should you generally not test what AWS owns?
Under shared responsibility, test your business logic, configuration, permissions, and payloads — verify config without invoking the underlying service.
Comments
Sign in with GitHub to join the discussion.