Construct small classes whose dependencies arrive through interfaces or constructor parameters.
The examples focus on the operation itself, including the important distinction: Hard-coded service creation couples tests to network or database infrastructure.
What It Does
Use the examples below to observe the behavior on a small dataset before extending it to application code.
Before You Start
Read the input requirements before applying this pattern to production data.
Example 1: Basic Use
The fake dependency provides a deterministic test without requiring a test framework.
<?php
interface Clock { public function today(): string; }
class FakeClock implements Clock { public function today(): string { return "2026-01-01"; } }
class Badge { public function __construct(private Clock $clock) {}
public function text(): string { return "Valid " . $this->clock->today(); }
}
echo (new Badge(new FakeClock()))->text();
Output from a successful local PHP 8.4 run:
Valid 2026-01-01
Example 2: Checking Behavior
The fake dependency provides a deterministic test without requiring a test framework. The injected fake supplies a different deterministic date for the test case.
<?php
interface Clock { public function today(): string; }
class FakeClock implements Clock { public function today(): string { return "2026-02-01"; } }
class Badge { public function __construct(private Clock $clock) {}
public function text(): string { return "Valid " . $this->clock->today(); }
}
echo (new Badge(new FakeClock()))->text();
Output from a successful local PHP 8.4 run:
Valid 2026-02-01
Example 3: Practical Example
The fake dependency provides a deterministic test without requiring a test framework. The injected fake supplies a different deterministic date for the test case.
<?php
interface Clock { public function today(): string; }
class FakeClock implements Clock { public function today(): string { return "2026-03-01"; } }
class Badge { public function __construct(private Clock $clock) {}
public function text(): string { return "Valid " . $this->clock->today(); }
}
echo (new Badge(new FakeClock()))->text();
Output from a successful local PHP 8.4 run:
Valid 2026-03-01
Important Behavior and Edge Cases
Tests should check externally observable behavior, not private implementation details.
Comparison with Related Approaches
| Approach | Difference |
|---|---|
| Writing Testable Classes Without a Framework | Construct small classes whose dependencies arrive through interfaces or constructor parameters. |
| Related API or alternative | Hard-coded service creation couples tests to network or database infrastructure. |
Common Beginner Mistakes
| Mistake | Correct approach |
|---|---|
| Ignoring the operation-specific limits | Tests should check externally observable behavior, not private implementation details. |
| Confusing it with a related API | Hard-coded service creation couples tests to network or database infrastructure. |
Key Takeaways
- Construct small classes whose dependencies arrive through interfaces or constructor parameters.
- Tests should check externally observable behavior, not private implementation details.
- Hard-coded service creation couples tests to network or database infrastructure.
Use Writing Testable Classes Without a Framework when its documented behavior matches the problem. Test the relevant edge cases in your own runtime before deploying it.