matthiasnoback/doctrine-dbal-test-service-provider
Install the Package Add the package via Composer in your test environment:
composer require --dev matthiasnoback/doctrine-dbal-test-service-provider
Extend Your Test Case Use the provided trait in your test class:
use Noback\PHPUnitTestServiceContainer\PHPUnit\TestCaseWithDoctrineDbalConnection;
class UserRepositoryTest extends TestCaseWithDoctrineDbalConnection
{
// Test methods here
}
Define Your Schema
Implement the createSchema() method to define tables/columns for testing:
protected function createSchema(): Schema
{
$schema = new Schema();
$schema->createTable('users')->addColumn('name', 'string');
return $schema;
}
Access the Connection
Inject the Doctrine\DBAL\Connection in test methods via $this->getConnection().
public function testFindUserByName()
{
$connection = $this->getConnection();
$connection->insert('users', ['name' => 'John Doe']);
$user = $this->repository->findBy(['name' => 'John Doe']);
$this->assertEquals('John Doe', $user->name);
}
Schema Setup
Define your schema in createSchema() once per test class. Reuse it across all test methods.
Per-Test Isolation Each test method runs against a fresh in-memory SQLite database (default). No shared state between tests.
Connection Injection
Prefer dependency injection over getConnection() for better test clarity:
public function testSomething(Connection $connection)
{
// Use $connection directly
}
Service Container Integration
Register the provider in phpunit.xml:
<phpunit>
<extensions>
<extension class="Noback\PHPUnitTestServiceContainer\PHPUnit\ServiceContainerExtension"/>
</extensions>
</phpunit>
Customizing the Database
Override the default SQLite setup by binding a custom Connection in your test case:
protected function getConnection(): Connection
{
return $this->getService('dbal.connection'); // Custom binding
}
Migrations for Complex Schemas
For large schemas, use Doctrine migrations in setUp():
public function setUp(): void
{
$connection = $this->getConnection();
$migration = new \Doctrine\DBAL\Migrations\Migration();
$migration->up($connection);
}
$connection->beginTransaction();
try {
// Test logic
$connection->commit();
} catch (\Exception $e) {
$connection->rollBack();
throw $e;
}
$user = User::factory()->create(['name' => 'Test']);
$connection->insert('users', ['name' => $user->name]);
Schema Mismatch Errors
createSchema() must match the actual database schema used in tests.Connection Leaks
$connection->close() in tearDown() if needed.Shared State in Tests
createSchema() is idempotent and each test method starts fresh.Inspect the Schema Dump the current schema for debugging:
$schema = $this->getConnection()->createSchemaManager()->createSchema();
echo $schema->toSql();
Enable Query Logging Log all queries to identify issues:
$connection->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
Custom Error Handling
Override getConnection() to add error handling:
protected function getConnection(): Connection
{
$connection = parent::getConnection();
$connection->connect();
return $connection;
}
Custom Database Drivers Replace SQLite with PostgreSQL/MySQL for testing:
protected function createConnection(): Connection
{
return DriverManager::getConnection([
'url' => 'mysql://user:pass@localhost/test_db',
]);
}
Dynamic Schema Generation Generate schemas dynamically based on test parameters:
protected function createSchema(): Schema
{
$schema = new Schema();
if ($this->hasOption('with_orders')) {
$schema->createTable('orders')->addColumn('user_id', 'integer');
}
return $schema;
}
Integration with Laravel’s DBAL Share the test connection with Laravel’s service container:
$this->app->instance(Connection::class, $this->getConnection());
Default Isolation Level
The provider uses READ UNCOMMITTED by default for performance. Override in createSchema():
$connection->setTransactionIsolation(Connection::TRANSACTION_READ_COMMITTED);
Foreign Key Constraints
Disable constraints in createSchema() if needed:
$connection->getDatabasePlatform()->getForeignKeyDefinitionSchemaSql();
Timezone Handling Set a consistent timezone to avoid date/time discrepancies:
$connection->getDatabasePlatform()->registerDoctrineTypeMapping('datetime', 'string');
How can I help you explore Laravel packages today?