czukowski/phpunit-mock-db

PHPUnit 的数据库抽象层模拟助手

9.0 2020-02-20 05:56 UTC

README

PHPUnit

一个用于数据库查询测试的模拟对象库,无需从 fixtures 初始化内存数据库。相反,测试代码执行的每个查询都可以设置为返回预定义的结果集、受影响的行数或最后插入 ID。所有这些都可以使用类似于 PHPUnit 模拟对象的熟悉界面。

安装

composer require czukowski/phpunit-mock-db

版本号遵循主要的 PHPUnit 版本号,因此对于给定的 PHPUnit N.x,您将获得此包的相应版本(这应该会自动发生)。

用法

在测试用例类中使用 Cz\PHPUnit\MockDB\MockTrait 特性,这将启用创建数据库模拟实例的方法。测试代码中使用的数据库抽象层必须使用一个“伪造”的驱动程序,该驱动程序实现了 Cz\PHPUnit\MockDB\DatabaseDriverInterface 接口和数据库抽象层的接口,此外,测试用例类必须实现 getDatabaseDriver 方法,该方法返回该驱动程序的实例。

注意:这仅覆盖了针对单个数据库连接的最简单和最常用的测试用例,并且特性已经相应地设计。如果需要同时模拟多个数据库连接或以不同的方式向测试代码中注入依赖项,则可能需要不同的 MockTrait 实现。但是,特性非常简单,特别是在与无论如何都需要实现的“伪造”驱动程序实现相比时,您可以克隆并调整特性以适应您的项目或提出一个完全不同的实现。

示例

任何 数据库查询上返回预定义的结果集

$this->createDatabaseMock()
    ->expects($this->any())
    ->willReturnResultSet([
        ['id' => 1, 'name' => 'foo'],
        ['id' => 2, 'name' => 'bar'],
    ]);

任何 数据库查询上返回预定义的结果集,并期望它恰好执行一次

$this->createDatabaseMock()
    ->expects($this->once())
    ->willReturnResultSet([
        ['id' => 1, 'name' => 'foo'],
        ['id' => 2, 'name' => 'bar'],
    ]);

在每次特定的数据库查询上返回预定义的结果集,并期望每个查询恰好执行一次

注意:查询期望设置的顺序不一定与查询执行的顺序相同。

另外注意:在查询约束中会忽略空格,因此可以从格式良好的文件中加载,这对于长而复杂的查询特别有用。

$mock = $this->createDatabaseMock();
$mock->expects($this->once())
    ->query('SELECT * FROM `t1`')
    ->willReturnResultSet([['id' => 1, 'name' => 'foo']]);
$mock->expects($this->once())
    ->query('SELECT * FROM `t2`')
    ->willReturnResultSet([['id' => 2, 'name' => 'bar']]);

期望混合查询,一些在特定的调用中(注意:SELECT 查询设置为返回空结果集)

$mock = $this->createDatabaseMock();
$mock->expects($this->at(1))
    ->query('INSERT INTO `t1` VALUES (1, "foo")')
    ->willSetLastInsertId(1);
$mock->expects($this->at(2))
    ->query('INSERT INTO `t1` VALUES (2, "bar")')
    ->willSetLastInsertId(2);
$mock->expects($this->once())
    ->query('SELECT * FROM `t1`')
    ->willReturnResultSet([]);

期望相同的查询恰好执行三次,并在每次连续调用上返回不同的最后插入 ID,注意这个查询是如何参数化的

$this->createDatabaseMock()
    ->expects($this->exactly(3))
    ->query('INSERT INTO `t1` VALUES (?, ?, ?)')
    ->with(['a', 'b', 'c'])
    ->willSetLastInsertId(1, 2, 3);

返回受影响的行数

$this->createDatabaseMock()
    ->expects($this->exactly(2))
    ->query('UPDATE `t1` SET `foo` = "bar" WHERE `id` = 1')
    ->willSetAffectedRows(1);

使用 PHPUnit 约束匹配 SQL 查询(注意:使用默认的 PHPUnit 约束时不会忽略空格)

$this->createDatabaseMock()
    ->expects($this->once())
    ->query($this->stringStartsWith('SELECT'))
    ->willReturnResultSet([['id' => 1, 'name' => 'foo']]);

使用连续调用存根构建器为 INSERT 查询设置连续调用上的不同结果

$this->createDatabaseMock()
    ->expects($this->exactly(4))
    ->query($this->stringStartsWith('INSERT'))
    ->onConsecutiveCalls()
    ->willSetLastInsertId(1)
    ->willSetLastInsertId(2)
    ->willThrowException(new RuntimeException('Deadlock'))
    ->willSetLastInsertId(3);

设置自定义回调来处理数据库查询(回调不需要返回任何内容)

$mock = $this->createDatabaseMock();
$mock->expects($this->any())
    ->query($this->stringStartsWith('INSERT'))
    ->willInvokeCallback(function ($invocation) {
        $invocation->setLastInsertId(1);
    });
$mock->expects($this->any())
    ->query($this->stringStartsWith('UPDATE'))
    ->willInvokeCallback(function ($invocation) {
        $invocation->setAffectedRows(0);
    });
$mock->expects($this->any())
    ->query($this->stringStartsWith('SELECT'))
    ->willInvokeCallback(function ($invocation) {
        $invocation->setResultSet([]);
    });

默认情况下,模拟对象会在执行未知(不匹配)查询时抛出异常,但可以禁用此功能

$mock = $this->createDatabaseMock();
$mock->setRequireMatch(FALSE);

许可证

本作品采用 MIT 许可证发布。有关详细信息,请参阅 LICENSE.md。