Fake Implementations

When mocking is not enough

Mocks are great for isolated unit tests. But sometimes you need a fake that behaves like the real thing.

When to use a fake
  • - The dependency has complex behavior you want to simulate
  • - Multiple tests need the same fake setup
  • - You want to assert on state, not just method calls
  • - The real service is slow, flaky, or costs money
The Contract
// app/Contracts/PaymentGateway.php
interface PaymentGateway
{
    public function charge(int $amount, string $token): array;
}
The Fake Implementation
// replaces real implementation in test e.g. StripePaymentGateway
            
// app/Services/Payment/FakePaymentGateway.php
class FakePaymentGateway implements PaymentGateway
{
    public function charge(int $amount, string $token): array
    {
        return [
            'id' => 'ch_' . uniqid(),
            'status' => 'succeeded',
            'amount' => $amount,
        ];
    }
}
The Controller Test with Fake
// tests/Feature/CheckoutControllerTest.php
class CheckoutControllerTest extends TestCase
{
    use RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();

        $this->app->bind(PaymentGateway::class, FakePaymentGateway::class);
    }

    #[Test]
    public function order_is_charged_successfully(): void
    {
        $response = $this->postJson('/checkout', [
            'total' => 100,
            'payment_token' => 'tok_visa',
        ]);

        $response->assertOk()
            ->assertJson([
                'status' => 'succeeded',
                'amount' => 100,
            ]);

        $this->assertDatabaseHas('orders', [
            'total' => 100,
            'payment_token' => 'tok_visa',
            'status' => 'paid',
        ]);
    }
}
Why fakes beat mocks
  • - Track state across multiple calls
  • - No fragile mock expectations
  • - Reusable across many tests
  • - Can be tested with contract tests

Make sure your fake behaves like the real thing. Use contract tests — run the same test suite against both the fake and the real implementation.

Read more by @jcergolj: Contract Tests