SPB Git

spb/ultra-sharp-agent-skills Public

Ultra-Sharp Agent Skills — a research-first skill-authoring system + 72 production-ready skills for AI agents.

Python 100%

# name: testing-backend-services description: Designs and writes tests for backend services — unit, integration with real dependencies in containers, contract tests, and minimal E2E — with deterministic, parallel-safe practices. Use when the user asks to test an API or service, write unit/integration/contract tests, fix flaky backend tests, set up testcontainers, or decide what to mock. Do not use for frontend or UI testing, or for load and performance testing.

# Testing Backend Services

# When to use / when NOT to use

  • Use for: test strategy and test code for services and APIs: unit, integration, contract, E2E; de-flaking; test data design.
  • Do NOT use for: browser/UI testing, or load/perf/soak testing — different tooling and goals.

# Core rules

  1. Shape tests as a pyramid. Many milliseconds-fast unit tests on pure logic; a solid layer of integration tests; one happy-path E2E per critical flow. Inverting it (E2E-heavy) buys slow, flaky suites.
  2. Integration tests hit real dependencies in containers. Spin up Postgres/Redis/broker via testcontainers.
    • ✅ test repository code against a real Postgres container
    • ❌ mock the database driver and assert SQL strings — that tests your mock.
  3. Mock only what you don't own (third-party HTTP APIs, clocks, randomness) — and pin those mocks with contract tests where possible.
  4. Test behavior, not implementation. Assert on outputs, state changes, and emitted events — not on which internal methods were called. Refactors must not break green tests.
  5. No sleeps. Poll with a timeout for async effects; freeze the clock for time logic; seed randomness. A test that needs sleep(2) is a race you scheduled.
    • wait_until(lambda: outbox.count() == 1, timeout=5)
    • time.sleep(2); assert outbox.count() == 1
  6. Each test is independent and parallel-safe: owns its data (unique IDs per test), never depends on execution order, cleans up via transaction rollback or per-test schema.
  7. Factories over shared fixtures. make_user(email=...) with overridable defaults beats a giant fixtures.sql that every test secretly depends on.
  8. Contract tests guard API boundaries: provider verifies it still satisfies consumer expectations (Pact or OpenAPI-based) on every CI run — cheaper than E2E across repos.

# Workflow

  1. Classify the change: pure logic → unit; touches DB/broker/HTTP edge → integration; crosses service boundary → contract; business-critical flow → one E2E.
  2. Write the test first at the lowest level that can catch the bug.
  3. Build test data with factories; give every entity a per-test unique key.
  4. Replace any sleep/order dependency with polling, frozen clocks, seeded RNG.
  5. Validate: run the suite twice — full run and --last-failed in random order (pytest -p randomly); both must pass. Run the new test 20× (pytest --count=20 -x) to prove it's not flaky.

# Edge cases & failure modes

  • Test passes locally, fails in CI → almost always shared state or timing; check for fixed ports, shared DB rows, real clock usage.
  • Container startup dominates runtime → reuse one container per session with per-test transactions/schemas, not one container per test.
  • Untestable code (network calls in constructors, global singletons) → refactor for injection first; don't monkey-patch around design problems.
  • Non-determinism sources (UUIDs, now(), env) → inject them; asserting on wall-clock values is a flake factory.

# References

Deeper recipes and gotchas: see references/patterns.md