Module 7 · Testing

Lesson 35 — E2E and contract testing

The full purchase flow under test and contracts that don't break.

Published
In this lesson
  1. Exercise 1 — The catalog
  2. Exercise 2 — The full purchase
  3. Exercise 3 — The flaky, hunted
  4. Exercise 4 — The contract
  5. Exercise 5 — The engine room
  6. Submit

The pyramid's dome, small and stable. No solutions.md before submitting.

Exercise 1 — The catalog

  1. List TicketFlow's business stories and mark the ones entering the E2E catalog (target: ≤ 8 for your scope). For each: the full HTTP flow and which dependency gets doubled.
  2. The ones that DON'T enter: list 5 edge cases you were tempted to push up to E2E and reassign them to their real layer (serializer→unit, permissions→integration, etc.).
  3. Run pytest -m e2e --collect-only: the catalog is measurable. How many? How long would it take at 20 s/test?

Exercise 2 — The full purchase

  1. Implement the complete test_compra_feliz (search → reserve → pay → confirm → ticket) with drenar_cola() and asserts on the final STATE (reservation CONFIRMED, mailbox 1, seats SOLD).
  2. The tragic story: declined payment → compensation (32). Asserts: state COMPENSATED, seat free again, failure email, webhook NOT sent (the third party doesn't get paid for a failed sale).
  3. The dispute story: two users, same seat, near-simultaneous payments → ONE CONFIRMED, the other compensated with a refund. The test of the guarantee the business sells.

Exercise 3 — The flaky, hunted

  1. Plant a flaky on purpose: a test doing time.sleep(1) and then asserting the saga's state (fails on slow machines). Switch it to wait_for with a 5 s deadline: does it run stable 10 times in a row? (pytest --count=10 or a loop).
  2. The state dump: add to wait_for the dump (saga state, pending eager-queue tasks, last 3 outbox events). Break the flow on purpose (the payment doesn't confirm): is the dump enough to diagnose without opening the IDE?
  3. The quarantine: define @pytest.mark.flaky (or your marker) that in CI re-runs once with a report. The rule: max 2 tests in quarantine and a mandatory issue. Write the rule into CONTRIBUTING.md.

Exercise 4 — The contract

  1. Write CONTRATO_FRONT with the 6 interactions TicketFlow's front uses (listing, detail, reserve, pay, status, cancel) and the parametrized test validating them.
  2. Break the contract on purpose: change total (int) to total (an object {amount, currency}) in the response. Which test goes red and after how many seconds? Now change the CONTRACT (the consumer accepts both for a sprint, 14: dual version) — how is that compatibility written in the test?
  3. The error contract (26): add the problem+json rules (type, title, status) to the contract. Test: the disputed seat's 409 satisfies the error contract.

Exercise 5 — The engine room

  1. Build the E2E compose (app + PG + Redis with healthchecks) and the clean reset between tests (truncation + Redis DB 15 flush). Measure: how long does the reset take? And "restarting the world" (compose down/up)? The difference is what you save ×N tests.
  2. The auth fixture (18): comprador_autenticado that creates the user, gets an access token and uses it in the client. No "manual" login in any test.
  3. The health metric: run the catalog 20 times (or use the CI runs if they exist) and compute the flaky_rate. < 1%? If not: which test goes to quarantine and why?

Submit

Paste the catalog, the 3 story tests (happy/tragic/dispute), the contract with the break's red and the flakiness metric. Next: Lesson 36 — Load testing (k6).