Phase 6 · Testing, Delivery & ProductionModule 42~68 min read

Contract, End-to-End & Resilience Testing

Verify system boundaries with database integration, API contracts, end-to-end workflows, fault injection, and performance smoke tests.

What you'll learn

Boundary failures emerge only when serialization, databases, networks, processes, and deployment behavior interact. Add API contracts, real dependency integration, end-to-end journeys, fault injection, and small performance gates.

By the end of this lesson, you'll be able to:

  • Verify API contracts
  • Test real infrastructure in isolation
  • Exercise critical user journeys
  • Inject faults and assert graceful behavior

Core mental model

Node.js becomes easier when you separate the JavaScript language from the runtime and the operating-system capabilities it exposes. Use this table as a decision guide.

ConceptWhat it meansDecision rule
Consumer contractAn agreed request/response interactionVerify compatibility when independently deployed clients depend on it
End-to-end testA journey through deployed boundariesKeep a small set for business-critical paths
Fault injectionControlled dependency delay or failureUse to prove timeouts, retries, fallbacks, and recovery

Professional workflow

Build and verify Node.js programs from the terminal in small, observable steps.

  1. Define the system boundary test boundary: inputs, outputs, invariants, ownership, and expected failures.
  2. Design the data or message contract before choosing implementation details.
  3. Implement the smallest correct path with dependencies passed explicitly.
  4. Add validation, failure translation, cleanup, and concurrency behavior.
  5. Verify the boundary with realistic data and at least one adversarial case.
  6. Measure or observe the behavior before optimizing or extracting abstractions.

Keep the feedback loop short

Run the smallest useful command after every meaningful change. Read the complete error message before editing again, and keep inputs and outputs visible while you learn.

Guided code lab

Test a timeout as product behavior

The test controls a slow dependency and asserts the public status, bounded latency, and correlation evidence.

resilience.test.js
test('returns 503 within the dependency budget', async () => {
  dependency.delayNextResponse(5_000);
  const started = performance.now();
  const response = await api.get('/reports/summary');
  assert.equal(response.status, 503);
  assert.ok(performance.now() - started < 1_500);
  assert.match(response.headers.get('x-request-id'), /.+/);
});

Production practice

Contract

Boundary tests fix schema, status, headers, side effects, timing budget, dependency assumptions, and cleanup.

Verification

Use fresh tenant data, disposable real services, controlled network faults, retry observation, and parallel CI execution.

Operations

Separate fast PR gates from scheduled deep suites, retain failure artifacts, and monitor flaky and performance regressions.

Common failure mode

An end-to-end suite that shares mutable data and implicit ordering becomes slow, flaky, and unable to localize failures.

Independent workshop

Create a production-confidence test lane for the Phase 5 service.

Your finished workshop must include:

  • OpenAPI compatibility check
  • Real database/cache tests
  • Three E2E journeys
  • Fault-injection cases
  • Load smoke gate
  • Failure artifacts

Definition of done

Run the happy path and at least two edge cases, keep responsibilities separated, and add a short README explaining how to run the program.

Recap & quick check

Key takeaways

  • Contracts protect consumers
  • Real dependencies expose semantics
  • E2E tests stay selective
  • Faults are requirements
  • Isolation prevents flakiness

Quick check

1. What should an E2E suite focus on?

2. Why inject latency?

3. What keeps tests independent?

Next: TypeScript for Production Node.js