Testing Strategy Overview
Phexium uses a multi-layer testing strategy with three test types: Unit, Integration, and Acceptance.
Test Pyramid
/\
/ \
/ AT \ Acceptance Tests (Behat)
/------\ → end-to-end user scenarios
/ IT \ Integration Tests (Pest)
/----------\ → component interactions
/ UT \ Unit Tests (Pest)
/--------------\ → isolated logic testing
Test Types
Unit Tests
Unit tests verify individual components in isolation.
Location: tests/Phexium/Unit/, tests/AppDemo/Unit/
Scope: Value Objects, Entities, Handlers, Collections
Integration Tests
Integration tests verify component interactions with real dependencies.
Location: tests/Phexium/Integration/, tests/AppDemo/Integration/
Scope: Repository implementations, Bus dispatching, Middleware chains
Acceptance Tests
Acceptance tests verify complete user scenarios with Behat (BDD).
Location: tests/AppDemo/Acceptance/, tests/AppStarter/Acceptance/
Scope: Full HTTP request/response cycles, User workflows
task tests:acceptance # All databases
task tests:acceptance:demoInMemory # InMemory only
task tests:acceptance:demoSqlite # SQLite only
Test Framework
Phexium uses Pest PHP for unit and integration tests:
test('Title rejects empty string', function (): void {
expect(fn () => Title::fromString(''))
->toThrow(InvalidArgumentException::class);
});
Running All Tests
This runs unit, integration, and acceptance tests with coverage merge.
Mutation Testing
Verify test quality by introducing code mutations:
See Mutation Testing for detailed guidance.
Test-Driven Development (TDD)
TDD is the recommended approach: write tests first, then implement. Follow the Red-Green-Refactor cycle.
The TDD Cycle
┌─────────────────────────────────────────────┐
│ │
│ ┌───────┐ ┌───────┐ ┌──────────┐ │
│ │ RED │───▶│ GREEN │───▶│ REFACTOR │ │
│ └───────┘ └───────┘ └──────────┘ │
│ ▲ │ │
│ └───────────────────────────┘ │
│ │
└─────────────────────────────────────────────┘
1. Red - Write Failing Test
- Write a test for the next small piece of functionality
- Test should fail (no implementation yet)
- Test describes expected behavior
2. Green - Make It Pass
- Write minimal code to make the test pass
- Don't over-engineer
- Just enough to satisfy the test
3. Refactor - Improve
- Clean up code while tests remain green
- Remove duplication
- Improve naming
- No new functionality
TDD Example
// 1. RED - Write test first
test('Title rejects empty string', function (): void {
expect(fn () => Title::fromString(''))
->toThrow(InvalidArgumentException::class);
});
// Run: FAIL (Title class doesn't exist)
// 2. GREEN - Minimal implementation
final readonly class Title
{
private function __construct(private string $value)
{
if ($value === '') {
throw new InvalidArgumentException('Title cannot be empty');
}
}
public static function fromString(string $value): self
{
return new self($value);
}
}
// Run: PASS
// 3. REFACTOR - Improve (add trim, use Assertion library)
TDD Benefits
- Tests document behavior - Tests serve as living documentation
- Design emerges from usage - API designed from consumer perspective
- High coverage by default - Every feature has tests
- Confidence in refactoring - Tests catch regressions immediately
When to Use TDD
| Use TDD | Skip TDD (add tests later) |
|---|---|
| New features | Exploratory/spike code |
| Bug fixes (reproduce first) | Infrastructure wiring |
| Value Objects and Entities | Framework configuration |
| Business logic in handlers | Proof of concept |
| Refactoring existing code |
Common TDD Mistakes
- Writing tests after implementation (loses design benefits)
- Testing implementation details instead of behavior
- Too many assertions per test (hard to diagnose failures)
- Skipping the refactor step (accumulates technical debt)
- Not running tests frequently (delays feedback)
Test Granularity
- One assertion per test (preferred) - Clear failure diagnosis
- Test one behavior at a time - Focused, maintainable tests
- Small, focused tests - Fast execution, easy to understand
- Descriptive test names - Document expected behavior
See Also
- Value Objects - What to unit test
- Entities - Business logic testing
- Commands & Handlers - Handler testing