PHP 怎么数据库断言

wen PHP项目 3

本文目录导读:

PHP 怎么数据库断言

  1. 使用 PHPUnit 数据库扩展(推荐)
  2. 使用 PHPUnit + PDO 直接断言
  3. 使用 Doctrine DBAL 和 Schema 断言
  4. 自定义断言辅助方法
  5. 使用 Laravel 的测试方法(如果是 Laravel 项目)
  6. 最佳实践建议

在 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,
        ]);
    }
}

最佳实践建议

  1. 使用事务:在测试中开启事务,测试结束后回滚,避免数据污染
  2. 分离测试库:使用单独的测试数据库,避免影响生产数据
  3. 使用 Fixtures:准备固定的测试数据
  4. 断言明确:断言具体的数据值,而不是仅仅检查数量
  5. 测试隔离:每个测试相互独立,不依赖测试数据
// 使用事务隔离测试
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)
  • 对测试速度和复杂性的要求

抱歉,评论功能暂时关闭!