Testing Guidelines

Testing Guidelines

Test Pyramid

  • Unit tests (fast, many): domain aggregates, value objects, handlers
  • Integration tests (medium, some): repositories, API endpoints, event handlers
  • E2E tests (slow, few): critical user flows

Directory Structure

tests/
├── Unit/
│   └── Module/{ModuleName}/
│       ├── Domain/
│       │   └── Model/{Aggregate}Test.php
│       └── Application/
│           └── Command/{Handler}Test.php
├── Integration/
│   └── Module/{ModuleName}/
│       ├── Infrastructure/
│       │   └── Persistence/{Repository}Test.php
│       └── Presentation/
│           └── Controller/{Controller}Test.php
└── Fixtures/
    └── {ModuleName}/
        └── {Fixture}Fixture.php

Unit Tests

  • No database, no framework kernel, no external services
  • Test aggregate creation, state transitions, invariant violations
  • Test value object validation and equality
  • Test command handlers with mocked repositories
  • Use PHPUnit data providers for edge cases

Integration Tests

  • Use Symfony’s WebTestCase or KernelTestCase
  • Real database (test database, reset between tests)
  • Use fixtures for test data setup
  • Test full request/response cycle for API endpoints
  • Assert: status code, response structure, database state

Naming

  • Test classes: {ClassUnderTest}Test
  • Test methods: test_{what_is_being_tested}_{expected_outcome}
  • Example: test_create_member_with_valid_data_succeeds
  • Example: test_create_member_without_email_throws_exception

Assertions

  • Be specific: assert exact values, not just “not null”
  • Assert side effects: events dispatched, database state changed
  • Assert error cases: correct exception type and message
  • Assert tenant scoping: data from other tenants is not returned

What to Test

  • Every aggregate: creation, state transitions, invariant violations
  • Every command handler: happy path + validation failures
  • Every API endpoint: success, validation error, auth required, not found
  • Event emission: correct events with correct payload
  • Privacy: privacy settings respected in queries

What NOT to Test

  • Getter/setter boilerplate
  • Framework internals
  • Third-party library behavior
  • Private methods (test through public API)