Skill

nw-test-organization-conventions

Test directory structure patterns by architecture style, language conventions, naming rules, and fixture placement. Decision tree for selecting test organization strategy.

From nw
Install
1
Run in your terminal
$
npx claudepluginhub nwave-ai/nwave --plugin nw
Tool Access

This skill uses the workspace's default tool permissions.

Skill Content

Test Organization Conventions

Core Principle

Test directory structure encodes architectural boundaries. If a developer cannot infer the architecture from the test tree alone, the organization is wrong.

Architecture-to-Organization Decision Tree

What is the project's architectural style?
|
+-- Hexagonal / Clean Architecture
|   --> Test-type-first: tests/{unit,integration,acceptance,e2e}/
|   --> Domain concepts nested within each type
|   --> Port contract tests in tests/integration/
|
+-- Vertical Slice
|   --> Co-located: features/{slice}/tests/
|   --> Cross-slice tests in top-level tests/cross_feature/
|
+-- Modular Monolith
|   --> Module-first: tests/modules/{module}/{unit,integration}/
|   --> Architecture tests per module (dependency rule enforcement)
|   --> Inter-module tests in tests/inter_module/
|
+-- Microservices
|   --> Per-service test tree: {service}/tests/{unit,integration,component,contract}/
|   --> Contract tests: consumer writes in own repo, provider verifies in own repo
|   --> Cross-service E2E in separate project: e2e-tests/
|
+-- Event-Driven
|   --> Test-type-first with event-specific categories
|   --> Unique: schema contract tests, idempotency tests, saga compensation tests
|
+-- CQRS
|   --> Command/Query split within each test type
|   --> Unique: projection tests (rebuild + idempotency)
|
+-- Layered (N-Tier)
|   --> Mirror source: tests mirror src layer hierarchy
|   --> Integration tests verify layer boundary contracts
|
+-- DDD (Tactical)
|   --> Bounded-context-first: tests/{context}/domain/aggregates/
|   --> Cross-context contract tests in tests/bounded_context_integration/

Comparative Summary

ArchitecturePrimary AxisSecondary AxisUnique Test TypesCo-located?
HexagonalTest typeDomain conceptPort contractNo
CleanTest typeArchitecture ringGatewayNo
LayeredMirror sourceTest typeLayer boundaryNo (Java: yes)
Vertical SliceFeatureTest typeCross-sliceYes
Modular MonolithModuleTest typeArchitecture, inter-moduleNo
MicroservicesPer-serviceTest typeContract (Pact)No
Event-DrivenTest typeEvent flow roleSchema, idempotency, sagaNo
CQRSCommand/QueryTest typeProjectionNo
DDDBounded contextBuilding blockAggregate, cross-contextNo

Mirror vs. Feature vs. Hybrid

StrategyBest ForStrengthsWeaknesses
Mirror sourceLayered, Clean, HexagonalPredictable paths, IDE navigation, package-private access (Java)Feature changes touch multiple dirs
Feature co-locatedVertical Slice, ReactAll feature code in one place, team ownershipCross-feature tests homeless, hard to run by tier
Hybrid (recommended)Most projectsType-first for CI stages, feature-nested withinSlightly deeper nesting

Hybrid pattern (type-first, then feature within):

tests/
  unit/
    features/
      order/test_create_order.py
      payment/test_process_payment.py
  integration/
    features/
      order/test_order_repository.py
  e2e/
    test_checkout_flow.py

Language-Specific Conventions

LanguageFile ConventionDiscovery RuleCo-located?
Python (pytest)test_*.py or *_test.pyPrefix/suffix matchSeparate tests/ (recommended) or inlined
TypeScript/JS (Jest)*.test.ts, *.spec.ts, __tests__/*.tstestMatch patternEither
Java (JUnit/Maven)*Test.java in mirrored packagesrc/test/java mirrors src/main/javaMirrored packages
Go*_test.go in same directoryLanguage-enforced co-locationAlways co-located
C# (xUnit/NUnit)*Tests.cs in parallel projectSeparate test projectSeparate project
Rust#[cfg(test)] mod tests inline + tests/Inline for unit, tests/ for integrationUnit: inline, Integration: separate

Python conftest.py Placement

Place conftest.py at the lowest directory where fixtures apply. Pytest discovers from outermost to innermost, enabling hierarchical fixture scoping:

tests/
  conftest.py              # Session fixtures: DB engine, app instance
  unit/
    conftest.py            # Unit-specific: auto-mock markers
  integration/
    conftest.py            # Integration: real DB fixtures
  acceptance/
    conftest.py            # Acceptance: feature file paths

BDD Feature Files

tests/
  features/                # Gherkin .feature files by domain
    order/
      place_order.feature
    payment/
      process_payment.feature
  step_defs/               # Step implementations by domain concept
    conftest.py
    order_steps.py
    payment_steps.py

Organize step definitions by domain concept, not by feature file. Shared steps across features prevent duplication.

Hexagonal Architecture Test Tiers

TierLocationTestsAdapters
Unittests/unit/Domain model, value objects, service layerNone (pure) or mock driven ports
Integrationtests/integration/Individual adapters against real infraReal infrastructure
Acceptancetests/acceptance/Use cases through driving portsIn-memory adapters
E2Etests/e2e/Full stack through HTTP/CLIReal adapters

Port contract tests: when multiple adapters implement one driven port, create shared contract test suite and run against each adapter.

tests/integration/
  test_repository_contract.py     # Abstract tests for RepositoryPort
  test_postgres_repository.py     # Runs contract against Postgres
  test_inmemory_repository.py     # Runs contract against in-memory

Fixture Organization

ScopeWhatPlacement
SessionExpensive setup (DB engine, app)Root conftest.py
ModuleSchema creation, service containersDirectory conftest.py
FunctionData cleanup, test isolationautouse=True in conftest.py
Shared across typesFactories, builderstests/conftest.py or tests/fixtures/

Anti-Patterns

Anti-PatternWhy It FailsFix
Flat tests/ with no structureCannot run by tier, no boundary visibilityOrganize by primary axis of architecture
Mirroring hex layers in test treeCouples tests to implementation structureOrganize by test type, not source structure
Acceptance tests alongside E2EAcceptance should use in-memory adapters and run fastSeparate: acceptance/ (fast) vs e2e/ (slow)
Feature-coupled step definitionsSteps for login.feature only usable thereOrganize steps by domain concept
Mixing test tiers in one directoryCannot run unit-only in pre-commitSeparate directories per tier
Cross-module imports in testsViolates module boundariesUse inter-module tests with event bus
E2E inside single microserviceThey span services by definitionMove to separate e2e-tests/ project
No contract tests between servicesMocks drift from realityConsumer-driven contract tests (Pact)
Stats
Parent Repo Stars299
Parent Repo Forks37
Last CommitMar 20, 2026