skills/333-333-333/agents/testing-strategy

testing-strategy

SKILL.md

When to Use

  • BEFORE writing any code — to plan what tests the feature needs
  • AFTER implementing a feature — to verify no testing level was skipped
  • When reviewing a PR — to check testing completeness
  • When writing acceptance criteria for a Jira story
  • When someone asks "what tests does this need?"

Core Philosophy

The Pyramid is Not Optional

Every feature touches multiple boundaries. Unit tests alone prove your logic works in isolation — they say NOTHING about whether the system works. A feature is NOT done until the appropriate test levels are covered.

Cost to fix a bug found at each level:

  Production    ████████████████████████  $$$$$
  Staging       ████████████████          $$$$
  E2E           ██████████████            $$$
  Integration   ████████████              $$
  Unit          ████                      $
  Code review   ██                        ¢

The higher up you catch a bug, the more expensive it is. The pyramid exists to push bug detection DOWN to the cheapest level possible — but you NEED the upper levels to catch what lower levels CANNOT.

What Each Level Proves

Level What it PROVES What it CANNOT prove
Unit Domain logic is correct, edge cases handled That components work together
Integration Real database works, SQL is correct, constraints hold That the API contract is correct
Contract API shape is stable, consumers can parse responses That the full flow works end-to-end
E2E Full request flow works through gateway → service → DB Performance under load
Load Service handles expected traffic, finds breaking point That it recovers from failures
Resilience Service degrades gracefully, recovers automatically — (this is the top)

Key insight: Each level catches a DIFFERENT class of bugs. They are not redundant — they are complementary.

The "It Works On My Machine" Ladder

"My function returns the right value"     → Unit test passes
"My SQL query runs without errors"        → Integration test passes
"My API returns the right JSON shape"     → Contract test passes
"The request works through the gateway"   → E2E test passes
"It works with 100 concurrent users"      → Load test passes
"It works when the database goes down"    → Resilience test passes

If you only have the first step, you only know the first thing. Every step you skip is a class of bugs you'll find in production.


Mandatory Test Levels by Change Type

Not every change needs every level. Use this matrix:

Change type Unit Integration Contract E2E Load Resilience
New domain entity / value object MUST
New use case / application service MUST
New repository implementation MUST MUST
New HTTP endpoint MUST MUST MUST
New external provider adapter MUST SHOULD
SQL migration (new table, alter) MUST
New service (full scaffolding) MUST MUST MUST MUST SHOULD
Gateway routing change MUST
Performance-sensitive endpoint MUST MUST
External call (FCM, SendGrid, etc.) MUST MUST
Bug fix MUST (regression) Depends

MUST = non-negotiable, the PR should not merge without it. SHOULD = strongly recommended, omit only with explicit justification.


The Feature Testing Workflow

When implementing ANY feature, follow this sequence:

1. PLAN    → Load this skill. Identify which levels apply (matrix above).
2. UNIT    → Load go-tdd. Write domain + application tests FIRST (TDD).
3. INFRA   → Implement. Make unit tests pass.
4. INTEG   → Load go-tdd. Test repo against real DB with testcontainers.
5. CONTRACT→ Load go-tdd. Test API response shape with contract tests.
6. E2E     → Load e2e-bruno. Create .bru files for happy + error paths.
7. LOAD    → Load go-load-testing. Run smoke test at minimum.
8. REVIEW  → Run the completion checklist (below).

Steps 4-7 are conditional based on the matrix. Steps 1-3 and 8 are ALWAYS required.


Test Directory Conventions

Tests live in specific locations based on their level:

api/{service}/
├── internal/
│   ├── domain/
│   │   └── *_test.go              # Unit tests (always with source)
│   ├── application/
│   │   └── *_test.go              # Unit tests (always with source)
│   └── infrastructure/
│       ├── repository/
│       │   └── postgres_integration_test.go  # Integration tests
│       └── handler/
│           └── contract_test.go    # Contract tests
├── tests/
│   └── e2e/                        # E2E tests (Bruno collections)
│       ├── bruno.json
│       ├── environments/
│       └── {feature}/
│           └── *.bru
└── tests/
    └── load/                       # Load tests (k6 scripts)
        └── smoke.js
Test Level Location Naming Build Tag
Unit Same package as source *_test.go None
Integration Same package as implementation *_integration_test.go //go:build integration
Contract infrastructure/handler/ *_contract_test.go None
E2E {service}/tests/e2e/ *.bru N/A (external tool)
Load {service}/tests/load/ *.js N/A (external tool)

E2E Test Conventions (Bruno)

Each service owns its E2E tests in {service}/tests/e2e/:

api/notification/tests/e2e/
├── bruno.json              # Workspace config
├── environments/
│   ├── local.bru          # localhost:8083
│   ├── development.bru    # dev environment
│   └── staging.bru        # staging environment
├── notification/          # Tests for this domain
│   ├── send-email.bru
│   ├── send-push.bru
│   ├── send-invalid-channel.bru
│   └── list-sent.bru
└── README.md

Why inside the service? E2E tests are versioned with the service they test. When you change an endpoint, you update its E2E test in the same PR.

Cross-service E2E (gateway flows): Put in api/gateway/tests/e2e/.

Skill Delegation

This skill does NOT contain implementation details. It delegates to specialized skills:

Testing level Delegate to skill What it covers
Unit tests go-tdd TDD cycle, table-driven tests, mocks, entity tests
Integration tests go-tdd Testcontainers, PostgreSQL, handler integration
Contract tests go-tdd Producer/consumer contract verification
E2E tests e2e-bruno Bruno collections, environments, CI integration
Load tests go-load-testing k6 smoke/load/stress/spike, thresholds
Resilience tests go-resilience Circuit breaker, retry, timeout, failure injection

Completion Checklist

Before marking a story as done, answer EVERY question:

See assets/completion-checklist.md for the full checklist to include in PR descriptions.


Common Excuses (and Why They're Wrong)

Excuse Reality
"Unit tests are enough" Unit tests prove logic works in isolation. They miss SQL bugs, serialization issues, and contract drift.
"We'll add integration tests later" Later never comes. The cost of adding them grows with every feature.
"E2E tests are slow" Bruno CLI tests run in seconds. Slower than unit tests, faster than debugging production.
"Load tests are overkill for MVP" The first time your service gets real traffic, you'll wish you ran a 30-second smoke test.
"We don't call external services yet" You will. Adding resilience patterns NOW costs 10 minutes. Adding them during an outage costs hours.
"The CI pipeline is already slow" Integration tests run only with -tags=integration. E2E runs as a separate job. Neither blocks unit tests.

Assets

File Description
assets/completion-checklist.md PR-ready checklist: every test level with pass/skip justification
assets/test-plan-template.md Template for planning test coverage before implementing a feature

Commands

# Unit tests (always)
go test ./...

# Integration tests (real database)
go test -tags=integration ./...

# E2E tests (Bruno) — run from service directory
cd api/notification/tests/e2e && bru run notification --env local

# Load tests (k6) — run from service directory
cd api/notification && k6 run tests/load/smoke.js

Resources

Weekly Installs
1
First Seen
4 days ago
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
kiro-cli1