Module 7 · Testing

Lesson 33 — Unit testing done well

pytest, mocks without abusing them and tests that survive refactors.

Published
In this lesson
  1. Exercise 1 — The suite audit
  2. Exercise 2 — The parametrization that survives
  3. Exercise 3 — Fakes before mocks
  4. Exercise 4 — The HOW tests, hunted
  5. Exercise 5 — The project's convention
  6. Submit

pytest over TicketFlow's services. No solutions.md before submitting.

Exercise 1 — The suite audit

  1. Run pytest --durations=10 (or manage.py test): list the 10 slowest tests. Classify each: legitimate unit / integration in disguise / real-time test.
  2. Hunt the anti-patterns in YOUR suite: a test that depends on another (run the whole file vs test by test), a literal-message assertion, a sleep, an ORM mock. Document each finding with the test's name.
  3. Migrate 3 tests from the old suite (manage.py test → pytest): the setUp → fixture, and one of them parametrized. Verify the whole suite stays green with both runners.

Exercise 2 — The parametrization that survives

  1. Write test_calcular_total with 6 parametrized cases (1 seat, 3, 10, 0 [error], -1 [error], 1000 [limit]) — case table on top, single assert below.
  2. The test must survive TWO refactors: rename calcular_total → precio_por and change its internal signature (extract _descuento_volumen). The tests are NOT edited. How many died? (expected: 0 if you tested the WHAT).
  3. Add the org's commission case (08) with parametrize over pytest.param(..., id="org-10-pct") — readable ids in the report.

Exercise 3 — Fakes before mocks

  1. Write the gateway_rechaza fixture (a configurable FakeGateway: declina_todo, timeout_pero_cobra — 32's one) and rewrite 3 tests that mocked the gateway with patch. How many lines were saved and which new assertion did it enable (the saga's state)?
  2. The repository test: rewrite the duplicate-reservation test (10) with InMemoryReservationRepository (25) WITHOUT a DB. How many ms does it run in? (measure with --durations).
  3. The one legitimate mock: verify that enviar_email_confirmacion.delay gets ENQUEUED via on_commit (here the interaction IS the contract). Write the test with captureOnCommitCallbacks and the task mock.

Exercise 4 — The HOW tests, hunted

  1. Write a BAD test on purpose: with patch("reservations.services.Seat.objects") as mock_seat:... mock_seat.filter.assert_called_once(). Now refactor reservar() (use select_for_update instead of filter): the good test stays green, the bad one dies. Paste the evidence of both.
  2. The literal message: find a test with a full-text assertion and change it to err.code. Verify: change the human error's text → the test stays green.

Exercise 5 — The project's convention

  1. Write the project's conftest.py with the 5 base fixtures (clock_fijo, evento, comprador, gateway, mailbox) and document each with a one-line docstring.
  2. Rename 5 tests to the test_<subject>_<scenario>_<result> convention and run the suite: pytest -k "reservar" must find them all by keyword.
  3. Measure services/ coverage: pytest --cov=reservations.services --cov-report=term. Which service is below 90% and which line is NOT tested? (the answer is not "add a test": it is "is that line dead code?").

Submit

Paste the durations report, the 3 migrated tests, the mocks→fakes before/after and the services coverage. Next: Lesson 34 — Integration testing.