PHP PDO 单元测试 Mock 实现方案有哪些

来源:编程网作者:菲律宾程序员头衔:程序员
导读:本期聚焦于小伙伴创作的《PHP PDO 单元测试 Mock 实现方案有哪些》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《PHP PDO 单元测试 Mock 实现方案有哪些》有用,将其分享出去将是对创作者最好的鼓励。

在PHP项目里,PDO是操作数据库的主流扩展,单元测试中如果直接依赖真实数据库,不仅会让测试执行速度变慢,还可能因为环境差异导致测试结果不稳定,因此Mock PDO相关对象是单元测试的常见实践。

手动编写PDO Mock类

手动编写Mock类是最基础的实现方式,我们可以创建一个继承PDO的类,重写需要的方法,返回预设的测试数据,避免真实数据库交互。

实现示例

<?php
// 自定义Mock PDO类
class MockPDO extends PDO {
    private $mockResults = [];

    public function __construct() {
        // 不需要真实连接,空实现构造函数
    }

    // 重写prepare方法,返回自定义的MockStatement对象
    public function prepare($statement, $options = []) {
        return new MockPDOStatement($this->mockResults);
    }

    // 设置预设的查询结果
    public function setMockResult($result) {
        $this->mockResults = $result;
    }
}

// 自定义Mock Statement类
class MockPDOStatement {
    private $mockResults;
    private $executed = false;

    public function __construct($mockResults) {
        $this->mockResults = $mockResults;
    }

    public function execute($params = []) {
        $this->executed = true;
        return true;
    }

    public function fetchAll($mode = PDO::FETCH_ASSOC) {
        if ($this->executed) {
            return $this->mockResults;
        }
        return [];
    }
}

// 业务类示例
class UserRepository {
    private $pdo;

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }

    public function getUsers() {
        $stmt = $this->pdo->prepare("SELECT * FROM users");
        $stmt->execute();
        return $stmt->fetchAll();
    }
}

// 单元测试示例
class UserRepositoryTest extends PHPUnitFrameworkTestCase {
    public function testGetUsers() {
        $mockPdo = new MockPDO();
        $mockPdo->setMockResult([
            ['id' => 1, 'name' => '张三'],
            ['id' => 2, 'name' => '李四']
        ]);
        $repo = new UserRepository($mockPdo);
        $users = $repo->getUsers();
        $this->assertCount(2, $users);
        $this->assertEquals('张三', $users[0]['name']);
    }
}
<?php

使用PHPUnit内置Mock功能

PHPUnit本身提供了创建Mock对象的能力,不需要手动编写完整的Mock类,可以直接生成PDO和PDOStatement的Mock对象,设置方法的返回值即可。

实现示例

<?php
use PHPUnitFrameworkTestCase;

class UserRepositoryTest extends TestCase {
    public function testGetUsersWithPHPUnitMock() {
        // 创建PDOStatement的Mock对象
        $mockStatement = $this->createMock(PDOStatement::class);
        // 设置execute方法返回true
        $mockStatement->method('execute')
            ->willReturn(true);
        // 设置fetchAll方法返回预设数据
        $mockStatement->method('fetchAll')
            ->willReturn([
                ['id' => 1, 'name' => '王五'],
                ['id' => 2, 'name' => '赵六']
            ]);

        // 创建PDO的Mock对象
        $mockPdo = $this->createMock(PDO::class);
        // 设置prepare方法返回上面的MockStatement对象
        $mockPdo->method('prepare')
            ->willReturn($mockStatement);

        $repo = new UserRepository($mockPdo);
        $users = $repo->getUsers();
        $this->assertCount(2, $users);
        $this->assertEquals('王五', $users[0]['name']);
    }
}
<?php

使用第三方Mock库

如果需要更灵活的Mock能力,比如模拟PDO的更多方法、处理复杂的数据库交互场景,可以借助像Mockery这样的第三方Mock库,它的语法更简洁,功能更丰富。

实现示例

<?php
use PHPUnitFrameworkTestCase;
use Mockery;

class UserRepositoryTest extends TestCase {
    protected function tearDown(): void {
        Mockery::close();
    }

    public function testGetUsersWithMockery() {
        // 创建PDO的Mock对象
        $mockPdo = Mockery::mock(PDO::class);
        // 创建PDOStatement的Mock对象
        $mockStatement = Mockery::mock(PDOStatement::class);

        // 设置prepare方法返回MockStatement
        $mockPdo->shouldReceive('prepare')
            ->with('SELECT * FROM users')
            ->andReturn($mockStatement);

        // 设置execute方法返回true
        $mockStatement->shouldReceive('execute')
            ->withNoArgs()
            ->andReturn(true);

        // 设置fetchAll方法返回预设数据
        $mockStatement->shouldReceive('fetchAll')
            ->with(PDO::FETCH_ASSOC)
            ->andReturn([
                ['id' => 1, 'name' => '孙七'],
                ['id' => 2, 'name' => '周八']
            ]);

        $repo = new UserRepository($mockPdo);
        $users = $repo->getUsers();
        $this->assertCount(2, $users);
        $this->assertEquals('孙七', $users[0]['name']);
    }
}
<?php

不同方案的选择建议

如果是简单的测试场景,手动编写Mock类足够使用,不需要额外依赖;如果项目已经使用PHPUnit,优先使用内置的Mock功能,减少第三方依赖;如果需要模拟复杂的数据库交互逻辑,比如多次查询、事务操作等,第三方Mock库会更合适,能降低Mock代码的编写复杂度。

无论选择哪种方案,核心目标都是隔离数据库依赖,让单元测试可以快速、稳定地执行,同时保证测试覆盖到业务逻辑的正确性。

PHPPDO单元测试Mock修改时间:2026-06-22 11:57:50

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。