mpyw/mockery-pdo
Experimental BDD-style PDO mocking library built on mockery/mockery. Define expectations for prepare/bind/execute and return rows via fetch/fetchAll in a fluent API, making PDO-based code easy to test on PHP 8.2+.
Installation Add the package via Composer (PHP 8.2+ required):
composer require --dev mpyw/mockery-pdo
Ensure mockery/mockery is also installed (required dependency).
First Use Case: Mocking a Database Connection
In a test file (e.g., DatabaseTest.php), set up a mock PDO instance:
use Mockery\MockInterface;
use Mpyw\MockeryPdo\MockeryPdo;
public function testDatabaseQuery()
{
// Create a mock PDO instance (PHP 8.2+)
$mockPdo = MockeryPdo::mock();
// Stub a query result
$mockPdo->shouldReceive('query')
->once()
->andReturnSelf();
$mockPdo->shouldReceive('fetch')
->once()
->andReturn(['id' => 1, 'name' => 'Test']);
// Use the mock in your class under test
$userRepository = new UserRepository($mockPdo);
$result = $userRepository->find(1);
$this->assertEquals('Test', $result['name']);
}
Key Files to Reference
src/MockeryPdo.php: Core class for mock creation.tests/: Example usage patterns.Mock Creation
Use MockeryPdo::mock() to generate a PDO-compatible mock (PHP 8.2+):
$mockPdo = MockeryPdo::mock();
Stubbing Queries Chain methods to simulate database behavior:
$mockPdo->shouldReceive('prepare')
->once()
->andReturnSelf();
$mockPdo->shouldReceive('execute')
->once()
->andReturn(true);
$mockPdo->shouldReceive('fetchObject')
->once()
->andReturn(new stdClass());
Handling Exceptions Simulate database errors:
$mockPdo->shouldReceive('query')
->andThrow(new PDOException('Connection failed'));
Integration with Laravel
Override Laravel’s DB facade or Illuminate\Database\Connection in tests:
// In a test case's setUp()
$this->app->instance('db.connection', $mockPdo);
Mocking Transactions Stub transaction methods:
$mockPdo->shouldReceive('beginTransaction')
->once()
->andReturn(true);
$mockPdo->shouldReceive('commit')
->once()
->andReturn(true);
PHP Version Requirement BREAKING: This release drops PHP 7.x support and requires PHP 8.2+. Update your test environment:
# Update your project's PHP version (e.g., in .phpunit.xml or CI config)
<phpunit bootstrap="vendor/autoload.php" php="8.2">
Method Chaining Assumptions
The mock assumes PDO method chaining (e.g., query()->fetch()). If your code uses static calls like PDO::getAttribute(), mock these explicitly:
$mockPdo->shouldReceive('getAttribute')
->with('PDO::ATTR_ERRMODE')
->andReturn(PDO::ERRMODE_EXCEPTION);
Case Sensitivity
PDO method names (e.g., fetchAll vs. fetch_all) must match exactly. Use shouldReceive('fetch_all') for legacy PDO.
Laravel-Specific Quirks
Connection interface, not PDO directly:
$mockConnection = Mockery::mock('overload:Illuminate\Database\Connection');
run() and get():
$mockConnection->shouldReceive('run')
->with('SELECT * FROM users')
->andReturn(1);
Performance in Large Tests Avoid over-stubbing. Focus on critical paths:
// Bad: Stubbing every method
$mockPdo->shouldReceive('*')->andReturnSelf();
// Good: Target specific interactions
$mockPdo->shouldReceive('prepare->execute->fetch')
->andReturnUsing(function () { ... });
Verify Stubbed Methods
Use Mockery::spy() to log unmocked calls:
$spy = Mockery::spy();
$mockPdo->shouldReceive('query')->andReturn($spy);
Check for Partial Mocks
If using MockeryPdo::partialMock(), ensure the original PDO instance is passed:
$realPdo = new PDO(...);
$partialMock = MockeryPdo::partialMock($realPdo);
Laravel’s Service Container Clear the container between tests if mocks persist:
$this->app->forgetInstance('db.connection');
Custom Mock Behavior
Extend MockeryPdo to add domain-specific stubs:
class CustomMockeryPdo extends MockeryPdo
{
public static function mockWithDefaults()
{
$mock = parent::mock();
$mock->shouldReceive('setAttribute')
->with('PDO::ATTR_DEFAULT_FETCH_MODE', PDO::FETCH_ASSOC)
->andReturn(true);
return $mock;
}
}
Integration with Pest
Use Pest’s mock() helper with MockeryPdo:
use function Pest\Laravel\mock;
$mockPdo = mock(MockeryPdo::class, 'mock');
Mocking PDOStream
For PDO streams (e.g., PDO::getAvailableDrivers()), stub statically:
Mockery::mock('overload:PDO')
->shouldReceive('getAvailableDrivers')
->andReturn(['mysql']);
How can I help you explore Laravel packages today?