eloquent/phony-phpunit
Integration of the Phony mocking/stubbing library with PHPUnit, providing helpers to use Phony in your test suite. Note: this package is no longer maintained; see the linked statement and consider alternatives or the main Phony repo.
Installation:
composer require --dev eloquent/phony-phpunit
Ensure your phpunit.xml or phpunit.php config includes the autoloader.
First Use Case: Replace a PHPUnit mock with a Phony stub in a test:
use Eloquent\Phony\Phony;
// Traditional PHPUnit
$mock = $this->createMock(SomeService::class);
$mock->method('fetchData')->willReturn([]);
// Phony alternative
$mock = Phony::mock(SomeService::class);
$mock->when('fetchData')->thenReturn([]);
Key Entry Points:
Phony::mock(): Create mock objects.Phony::stub(): Create stubs for predictable return values.Phony::spy(): Spy on real objects without mocking.anInstanceOf(), emptyValue().Pattern: Replace external services (e.g., APIs, databases) with mocks.
$mock = Phony::mock(ApiClient::class);
$mock->when('getUser', ['id' => 1])->thenReturn(['name' => 'John']);
$service = new UserService($mock);
$this->assertEquals('John', $service->getUserName(1));
Laravel Example:
$mock = Phony::mock(UsersRepository::class);
$mock->when('find', [1])->thenReturn(new User());
$this->app->instance(UsersRepository::class, $mock);
Pattern: Use stubs for methods with fixed return values (e.g., config, constants).
$stub = Phony::stub(Logger::class);
$stub->when('log')->thenReturn(true);
Pattern: Spy on real objects to verify interactions (e.g., event dispatchers).
$spy = Phony::spy(new EventDispatcher());
$spy->shouldReceive('dispatch')->with('event.name');
Pattern: Leverage Phony’s type hints for safer mocks.
$mock = Phony::mock(Collection::class);
$mock->when('first')->thenReturn(anInstanceOf(Model::class));
Service Container: Bind mocks to the container for dependency injection:
$this->app->bind(UsersRepository::class, function () {
return Phony::mock(UsersRepository::class)
->when('find', [1])->thenReturn(new User());
});
Eloquent Models: Stub model queries to avoid database hits:
$stub = Phony::stub(User::class);
$stub->when('find', [1])->thenReturn(new User(['name' => 'Test']));
Partial Mocks: Mock only specific methods of a class:
$partialMock = Phony::partialMock(Service::class);
$partialMock->when('methodToMock')->thenThrow(new Exception());
Exception Testing:
$mock = Phony::mock(Service::class);
$mock->when('fail')->thenThrow(new RuntimeException('Error'));
$this->expectException(RuntimeException::class);
$mock->fail();
Callback-Based Responses:
$mock = Phony::mock(Calculator::class);
$mock->when('add')->then(function ($a, $b) {
return $a + $b + 1; // Dynamic logic
});
Trait for Reusable Mocks:
trait MocksUsersRepository {
protected function mockUsersRepository(): UsersRepository {
return Phony::mock(UsersRepository::class)
->when('find', [1])->thenReturn(new User())
->when('all')->thenReturn(collect());
}
}
Data Providers: Combine with PHPUnit’s data providers for parameterized tests:
public function testAddWithDataProvider() {
$mock = Phony::mock(Calculator::class);
$mock->when('add')->then(function ($a, $b) { return $a + $b; });
$this->assertEquals(5, $mock->add(2, 3));
}
Archived Status:
PHPUnit 9.x Only:
phpunit.xml:
<phpunit bootstrap="vendor/autoload.php">
<php>
<ini name="error_reporting" value="-1" />
</php>
</phpunit>
Strict Typing Issues:
emptyValue() as a fallback:
$mock->when('getFinalClassInstance')->thenReturn(emptyValue());
Self-Referential Stubs:
self by default (breaking change in v3.0.0). Adjust expectations:
// Old (may fail):
$stub = Phony::stub(Service::class);
$stub->when('getSelf')->thenReturn($stub);
// New (explicit):
$stub = Phony::stub(Service::class);
$stub->when('getSelf')->thenReturn($stub->getSelf());
Coverage Tools:
--coverage-filter:
phpunit --coverage-filter tests/
Verify Mock Interactions:
shouldReceive() to enforce method calls:
$mock->shouldReceive('criticalMethod')->once();
Inspect Stubbed Values:
$mock->when('getData')->then(function () {
return ['debug' => 'value'];
});
Clear Mocks Between Tests:
tearDown():
protected function tearDown(): void {
Phony::reset();
parent::tearDown();
}
Type Hinting Errors:
$mock->when('getUser')->thenReturn(new User()); // Avoid emptyValue()
Custom Matchers:
Phony::matcher('isEven', function ($value) {
return $value % 2 === 0;
});
$mock->when('check', isEven())->thenReturn(true);
Global Mocks:
setUp():
protected function setUp(): void {
Phony::stub(Logger::class)->when('log')->thenReturn(false);
parent::setUp();
}
Integration with Laravel Factories:
$mock = Phony::mock(User::class);
$mock->when('find', [1])->thenReturn(User::factory()->create());
Hybrid Mocks:
$mock = Phony::mock(Service::class);
$this->assertInstanceOf(Model::class, $mock->getModel());
eloquent/phony-phpunit is listed under require-dev in composer.json:
"require-dev
How can I help you explore Laravel packages today?