Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use when designing a test directory layout for a given architecture: pick the exact tier names, per-architecture top-level dirs, and named test categories this convention mandates.
.claude/skills/test-organization-conventions/SKILL.md| Model | Eval pass | Runs |
|---|---|---|
| gemini-3.6-flash | 100% | 7 |
| Model | Lift | Δ tokens | Δ turns | Cases | Verified |
|---|---|---|---|---|---|
| gemini-3.5-flashbest | +58% | — | 0% | 24 | 86d ago |
| gemini-3.6-flash | +55% | +98% | 0% | 22 | 54d ago |
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | — | — |
| case-10 | ✗→✓ | ▲ Improved | — | — |
| case-17 | ✗→✓ | ▲ Improved | — | — |
| case-14 | ✗→✓ | ▲ Improved | — | — |
| case-12 | ✗→✓ | ▲ Improved | — | — |
Enforces a fixed, architecture-driven test directory taxonomy: the exact tier names and order, the per-architecture top-level directories, the named test categories, and fixture/BDD placement. Apply whenever laying out (or auditing) a test tree so the architecture is inferable from the tree alone.
Use exactly four tiers, named and ordered: tests/unit/, tests/integration/, tests/acceptance/, tests/e2e/.
acceptance/ is a SEPARATE tier from e2e/ — never collapse them.acceptance/ runs through the application's driving ports against in-memoryadapters and must be FAST (suitable for every CI run / pre-merge).
e2e/ runs the full stack through real adapters (real DB, HTTP, queues)and is slow.
the source rings/layers in the test tree.
| Architecture | Layout | Cross-boundary tests | Extra | |---|---|---|---| | Hexagonal / Clean | tests/{unit,integration,acceptance,e2e}/ | — | type-first, not ring-mirrored | | Layered (N-tier) | test tree MIRRORS source layer hierarchy | — | integration tests at layer boundaries | | Modular monolith | tests/modules/{module}/{unit,integration}/ | tests/inter_module/ | one dependency-rule (architecture) test per module | | Vertical slice | features/{slice}/tests/ (co-located) | tests/cross_feature/ | — | | Microservices | {service}/tests/{unit,integration,component,contract}/ | e2e-tests/ (separate top-level project) | consumer-driven contracts | | DDD (tactical) | tests/{context}/domain/aggregates/ | tests/bounded_context_integration/ | bounded-context-first |
that the module does not import across its boundary.
e2e-tests/, never inside any one service's tree.
domain/aggregates/ within.payload schema compatibility), idempotency tests (re-delivering the same event has no extra effect), and saga-compensation tests (compensating / rollback steps fire when a saga step fails). Keep them inside the test-type- first tree.
tests that cover BOTH projection rebuild from the event stream AND idempotency of re-applying events.
ONE shared/abstract port-contract test suite and run it against EACH adapter implementation. Place it under tests/integration/.
The consumer writes and owns the contract in its OWN repo; the provider verifies that contract in the provider's OWN repo. Mocks alone drift from reality.
.feature files under tests/features/{domain}/.tests/step_defs/.order_steps.py,payment_steps.py), NOT one step-def file per feature file. Shared steps then serve multiple features without duplication.
tests/conftest.py.
conftest.py sits at the LOWEST directory where its fixturesapply (pytest discovers outermost→innermost, giving hierarchical scoping).
tests/fixtures/ directory.directory's conftest; Function (cleanup/isolation)→autouse=True in the nearest conftest.
Type-first for CI stages, feature-nested within: tests/unit/features/{domain}/, tests/integration/features/{domain}/, tests/e2e/ for cross-cutting flows.
| Language | File pattern | Placement | |---|---|---| | Python (pytest) | test_*.py or *_test.py | separate tests/ (recommended) | | TS/JS (Jest) | *.test.ts, *.spec.ts, __tests__/ | either | | Java (JUnit/Maven) | *Test.java | src/test/java MIRRORS src/main/java package | | Go | *_test.go | same directory (language-enforced co-location) | | C# (xUnit/NUnit) | *Tests.cs | separate parallel test project | | Rust | #[cfg(test)] mod tests + tests/ | unit inline, integration in tests/ |
BEFORE: tests/{unit, integration, e2e}/ (acceptance folded into e2e, all slow). AFTER:
tests/
unit/ # pure domain, fast
integration/ # adapters vs real infra
acceptance/ # driving ports + in-memory adapters, FAST
e2e/ # full stack + real adapters, slowBEFORE (mirrors the architecture rings):
tests/entities/ tests/use_cases/ tests/interface_adapters/ tests/frameworks/AFTER (test-type-first):
tests/unit/ tests/integration/ tests/acceptance/ tests/e2e/BEFORE: tests/orders/, tests/billing/ (flat, no tiers, no boundary test). AFTER:
tests/modules/orders/unit/ tests/modules/orders/integration/
tests/modules/orders/test_dependencies.py # dependency-rule test
tests/modules/billing/unit/ tests/modules/billing/integration/
tests/inter_module/test_orders_billing.py # cross-moduleBEFORE: tests/test_checkout.py (tests pulled out of the slice). AFTER:
features/checkout/tests/ # co-located with the slice
features/cart/tests/
tests/cross_feature/test_cart_to_checkout.pyBEFORE: payment-service/tests/e2e/test_full_purchase.py (E2E inside one service). AFTER:
payment-service/tests/{unit,integration,component,contract}/
e2e-tests/test_full_purchase.py # SEPARATE top-level projectBEFORE: tests/test_order_aggregate.py (flat, context invisible). AFTER:
tests/ordering/domain/aggregates/test_order.py
tests/billing/domain/aggregates/test_invoice.py
tests/bounded_context_integration/test_ordering_billing.pyBEFORE: tests/unit/ + tests/integration/ only. AFTER: add named categories schema_contract/, idempotency/, saga_compensation/ (e.g. a saga-compensation test asserts that when ReserveStock succeeds but ChargeCard fails, the ReleaseStock compensation fires).
BEFORE: tests/test_read_model.py asserts one query result. AFTER: split tests/unit/command/ vs tests/unit/query/; add tests/integration/projection/test_rebuild.py (replays the event stream and checks the projection) and test_idempotent_apply.py (applies the same events twice, asserts the read model is unchanged).
BEFORE: test_postgres_repo.py and test_inmemory_repo.py each re-write the same assertions independently. AFTER:
tests/integration/test_repository_contract.py # abstract suite for RepositoryPort
tests/integration/test_postgres_repository.py # runs contract vs Postgres
tests/integration/test_inmemory_repository.py # runs contract vs in-memoryBEFORE: provider repo holds a mock of the consumer and tests against it. AFTER: consumer repo defines & owns the contract (e.g. a Pact file); provider repo runs provider-verification against that published contract.
BEFORE: tests/step_defs/login_feature_steps.py, tests/step_defs/checkout_feature_steps.py (one file per feature). AFTER:
tests/features/auth/login.feature
tests/features/order/place_order.feature
tests/step_defs/auth_steps.py # by domain concept
tests/step_defs/order_steps.py
tests/step_defs/conftest.pyBEFORE: a db_engine session fixture duplicated inside each tier's conftest. AFTER:
tests/conftest.py # db_engine, app (session)
tests/integration/conftest.py# real-DB fixtures
tests/fixtures/factories.py # shared buildersorganized by its primary architectural axis, but classic N-tier mirrors the source layer hierarchy; integration tests then verify layer-boundary contracts.
src/test/java parallelssrc/main/java) even under hexagonal — the package mirror is a language rule, not a violation of R1's "don't mirror rings."
*_test.go beside the code); you cannot apply theseparate-tests/ layout for Go unit tests. Use Go's package_test external package for black-box tests; integration tests can still live in a tests/ dir.
own {unit,integration,acceptance,e2e} tiers AND adds event categories, while cross-service E2E still goes to the separate e2e-tests/ project.
meaningful it belongs in integration/ or e2e/, not acceptance/ — acceptance must stay on in-memory adapters to remain fast.
push it down to that tier's conftest so unrelated tiers don't pay for it.
e2e-tests/ project.tests/inter_module/.tests/cross_feature/.against each adapter, under tests/integration/.
tests/{unit,integration,e2e}/ tree and omitting theacceptance/ tier entirely.
coupling tests to implementation.
tests/cross/, tests/shared/) instead ofthe mandated inter_module/, cross_feature/, bounded_context_integration/.
idempotency, or saga-compensation categories.
idempotency.
.feature file, causing duplicated shared steps.inter_module/, cross_feature/,bounded_context_integration/, separate e2e-tests/.
tests/integration/.builders in tests/fixtures/.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.5-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +55 percentage points is the difference between those two pass rates over the 22 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.5-flash | verified | 6/27/2026 | +58% |
Other measured skills in the registry, with their headline benchmark lift.