本文目录导读:

- 使用 PHPUnit 数据库扩展(推荐)
- 使用 PHPUnit + PDO 直接断言
- 使用 Doctrine DBAL 和 Schema 断言
- 自定义断言辅助方法
- 使用 Laravel 的测试方法(如果是 Laravel 项目)
- 最佳实践建议
在 PHP 中进行数据库断言主要有以下几种方式和工具:
使用 PHPUnit 数据库扩展(推荐)
安装
composer require --dev phpunit/dbunit
基本用法
<?php
use PHPUnit\Framework\TestCase;
use PHPUnit\DbUnit\TestCaseTrait;
class UserDaoTest extends TestCase
{
use TestCaseTrait;
private static $pdo;
// 数据库连接配置
public function getConnection()
{
if (self::$pdo == null) {
self::$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', 'password');
}
return $this->createDefaultDBConnection(self::$pdo, 'test');
}
// 测试数据
public function getDataSet()
{
return $this->createXMLDataSet(dirname(__FILE__) . '/fixtures/users.xml');
}
public function testUserExists()
{
// 执行数据库操作
$dao = new UserDao();
$dao->createUser('John', 'john@example.com');
// 断言数据库中的记录
$queryTable = $this->getConnection()->createQueryTable(
'users',
'SELECT * FROM users WHERE email = "john@example.com"'
);
// 构建期望数据
$expectedTable = $this->createArrayDataSet([
'users' => [
['id' => 1, 'name' => 'John', 'email' => 'john@example.com'],
]
])->getTable('users');
$this->assertTablesEqual($expectedTable, $queryTable);
}
public function testRowCount()
{
$dao = new UserDao();
$dao->deleteUser(1);
$this->assertEquals(2, $this->getConnection()->getRowCount('users'));
}
}
使用 PHPUnit + PDO 直接断言
<?php
use PHPUnit\Framework\TestCase;
class DatabaseAssertionsTest extends TestCase
{
private $pdo;
protected function setUp(): void
{
$this->pdo = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
public function testDatabaseContainsRecord()
{
// 执行测试操作
$stmt = $this->pdo->prepare('INSERT INTO products (name, price) VALUES (?, ?)');
$stmt->execute(['Test Product', 19.99]);
// 断言记录存在
$stmt = $this->pdo->prepare('SELECT COUNT(*) FROM products WHERE name = ?');
$stmt->execute(['Test Product']);
$count = $stmt->fetchColumn();
$this->assertEquals(1, $count, '记录应该已插入到数据库');
}
public function testSpecificValues()
{
$productId = 1;
$stmt = $this->pdo->prepare('SELECT * FROM products WHERE id = ?');
$stmt->execute([$productId]);
$product = $stmt->fetch(PDO::FETCH_ASSOC);
// 断言具体字段值
$this->assertEquals(19.99, $product['price']);
$this->assertEquals('Test Product', $product['name']);
$this->assertNotNull($product['created_at']);
}
public function testRecordDoesNotExist()
{
$productId = 999; // 不存在的ID
$stmt = $this->pdo->prepare('SELECT COUNT(*) FROM products WHERE id = ?');
$stmt->execute([$productId]);
$count = $stmt->fetchColumn();
$this->assertEquals(0, $count, '记录不应该存在');
}
}
使用 Doctrine DBAL 和 Schema 断言
<?php
use Doctrine\DBAL\DriverManager;
use PHPUnit\Framework\TestCase;
class SchemaAssertionsTest extends TestCase
{
private $conn;
protected function setUp(): void
{
$this->conn = DriverManager::getConnection([
'driver' => 'pdo_mysql',
'host' => 'localhost',
'dbname' => 'test',
'user' => 'root',
'password' => 'password',
]);
}
public function testTableExists()
{
$sm = $this->conn->getSchemaManager();
$this->assertTrue($sm->tablesExist(['users']), 'users表应该存在');
}
public function testColumnExists()
{
$sm = $this->conn->getSchemaManager();
$columns = $sm->listTableColumns('users');
$this->assertArrayHasKey('id', $columns);
$this->assertArrayHasKey('name', $columns);
$this->assertArrayHasKey('email', $columns);
}
public function testColumnType()
{
$sm = $this->conn->getSchemaManager();
$column = $sm->listTableColumns('users')['price'];
$this->assertEquals('decimal', $column->getType()->getName());
}
}
自定义断言辅助方法
<?php
trait DatabaseAssertions
{
protected $pdo;
protected function assertTableContains($table, array $expectedRows, $message = '')
{
$sql = "SELECT * FROM {$table}";
$actualRows = $this->pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
foreach ($expectedRows as $expectedRow) {
$this->assertContains(
$expectedRow,
$actualRows,
$message ?: "Table {$table} should contain the expected row"
);
}
}
protected function assertRowCount($table, $expectedCount, $where = '', $message = '')
{
$sql = "SELECT COUNT(*) FROM {$table}";
if ($where) {
$sql .= " WHERE {$where}";
}
$actualCount = $this->pdo->query($sql)->fetchColumn();
$this->assertEquals(
$expectedCount,
(int)$actualCount,
$message ?: "Expected {$expectedCount} rows in {$table} but got {$actualCount}"
);
}
}
// 使用示例
class ProductTest extends TestCase
{
use DatabaseAssertions;
protected function setUp(): void
{
$this->pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
}
public function testProductTable()
{
$this->assertTableContains('products', [
['name' => 'Phone', 'price' => 599.99],
['name' => 'Laptop', 'price' => 999.99],
]);
$this->assertRowCount('products', 2, 'active = 1');
}
}
使用 Laravel 的测试方法(如果是 Laravel 项目)
<?php
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class UserTest extends TestCase
{
use RefreshDatabase;
public function testUserCanBeCreated()
{
$response = $this->post('/api/users', [
'name' => 'John Doe',
'email' => 'john@example.com',
]);
$this->assertDatabaseHas('users', [
'name' => 'John Doe',
'email' => 'john@example.com',
]);
$this->assertDatabaseCount('users', 1);
$this->assertDatabaseMissing('users', [
'email' => 'nonexistent@example.com',
]);
}
public function testDatabaseTransactions()
{
// 使用 create 方法
$user = User::factory()->create();
$this->assertDatabaseHas('users', [
'id' => $user->id,
'name' => $user->name,
]);
}
}
最佳实践建议
- 使用事务:在测试中开启事务,测试结束后回滚,避免数据污染
- 分离测试库:使用单独的测试数据库,避免影响生产数据
- 使用 Fixtures:准备固定的测试数据
- 断言明确:断言具体的数据值,而不是仅仅检查数量
- 测试隔离:每个测试相互独立,不依赖测试数据
// 使用事务隔离测试
class ProductTest extends TestCase
{
private $pdo;
protected function setUp(): void
{
$this->pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$this->pdo->beginTransaction();
}
protected function tearDown(): void
{
$this->pdo->rollBack();
}
public function testCreateProduct()
{
// 测试逻辑...
$this->pdo->exec("INSERT INTO products (name) VALUES ('Test')");
$count = $this->pdo->query("SELECT COUNT(*) FROM products")->fetchColumn();
$this->assertEquals(1, $count);
// 测试结束后自动回滚
}
}
选择哪种方式取决于:
- 项目是否使用框架(Laravel/Symfony)
- 测试框架类型(PHPUnit/Pest)
- 数据库类型(MySQL/PostgreSQL/SQLite)
- 对测试速度和复杂性的要求