【问题标题】:How to get a mock & PDO working with PHPUnit on a SQL INSERT statement如何在 SQL INSERT 语句上使用 PHPUnit 获得模拟和 PDO
【发布时间】:2021-06-11 11:32:13
【问题描述】:

我有一个名为ip_requestTest.php 的测试类,它具有以下功能:

public function testSetTimestampCount()
    {

        $validData = [
            'id' => '99',
            'ip' => '127.0.0.1',
            'timestamp' => '2021-06-03 10:28:43.000'
        ];
        $table = 'ip';

        $stmt = $this->createMock(\PDOStatement::class);
        $stmt->expects($this->once())
            ->method('execute')
            ->with($validData)
            ->willReturn(true);

        global $pdo;
        $pdo = $this->createMock('PDO');
        $pdo->expects($this->once())
            ->method('prepare')
            ->with("INSERT INTO {$table} id,address,timestamp VALUES (:ip, :id, :timestamp)")
            ->willReturn($stmt);

        $ipStmt = new ip_request();
        $ipStmt->setTimestampCount($validData);

    }

现在最后一行是问题,$ipStmt->setTimestampCount($validData);

我实际的 setTimestampCount() 函数不只接受一个参数。

看起来像这样:

    function setTimestampCount($ip,$id,$conn)
    {

        $query = $conn->prepare ("INSERT INTO `ip` (`id`, `address` ,`timestamp`)VALUES (:id,:ip,CURRENT_TIMESTAMP)");
        $query ->bindValue(':id', $id, PDO::PARAM_INT);
        $query ->bindValue(':ip', $ip, PDO::PARAM_STR);
        $query->execute();

    }

任何人都可以帮助我,或者给我任何关于如何正确测试此功能的线索吗?

如何将数据包含到测试函数调用中?

谢谢

更新 1:

尝试了 splat 运算符的建议,还删除了时间戳插入,因为这可以通过插入数据自动完成。

仍然无法正常工作,我得到的错误是 Too few arguments to function 2 passed, exactly 3 expected. 我显然知道这意味着什么,但是 splat 操作员如何处理这个问题?如果有的话?

【问题讨论】:

  • 你想在这里测试什么?
  • setTimestampCount() 函数谢谢,但就像我解释的那样,它需要 3 个参数,其中一个是实际连接,还有 id 和 ip,但我似乎无法弄清楚如何在测试期间传递这些变量,而不是数组 $validData,这是我希望能够作为测试的一部分传递的。
  • 使用 splat 运算符 ... 它是 $ipStmt->setTimestampCount(...$validData);
  • 试过了,并给出了更新,当我使用该行时仍然无法正常工作。

标签: php sql pdo phpunit


【解决方案1】:

您的方法 setTimestampCount($ip,$id,$conn) 需要这 3 个参数。

你传入的3是$validData的内容,即

'id' => '99',
'ip' => '127.0.0.1',
'timestamp' => '2021-06-03 10:28:43.000'

相反,您需要从该数组中删除时间戳参数,并将其替换为您的模拟连接。

其次,你正在这样做:

$stmt->expects($this->once())
        ->method('execute')
        ->with($validData)
        ->willReturn(true);

但是execute 方法不带参数:

$query->execute();

它也不返回任何东西(或者至少你没有将它返回的任何东西分配给变量),所以没有必要做->willReturn(true);

所以上面应该只是:

$stmt->expects($this->once())
        ->method('execute');

测试时间戳也没有意义。您应该做的就是检查您传递给prepare 方法的字符串。所以你需要检查两个字符串看起来是否相同。目前您正在测试的字符串是:

"INSERT INTO `ip` (`id`, `address` ,`timestamp`)VALUES (:id,:ip,CURRENT_TIMESTAMP)"

你正在指定

"INSERT INTO {$table} id,address,timestamp VALUES (:ip, :id, :timestamp)

所以诸如缺少()、参数之间的间距差异等都会导致此测试失败。它们必须相同。

最后,您实际上并没有测试ipid 或时间戳值是什么。 prepare 语句实际上并没有使用它们;它只有字符串:id,:ip,CURRENT_TIMESTAMP

您还应该真正测试bindValue 方法,它确实使用了 ip 和 id 值。由于您要调用它两次,这会稍微复杂一些,但是您可以使用PHPUnit's withConsecutive method 来处理这个问题:

$stmt->expects($this->exactly(2))
     ->method('bindValue')
     ->withConsecutive(
         [':id', $id, PDO::PARAM_INT],
         [':ip', $ip, PDO::PARAM_STR]
     );

所以我想说这个方法应该是这样的:

public function testSetTimestampCount()
{
    $table = 'ip';
    $id = '99';
    $ip => '127.0.0.1';
    
    $stmt = $this->createMock(\PDOStatement::class);
    $stmt->expects($this->once())
        ->method('execute');
        
    $stmt->expects($this->exactly(2))
        ->method('bindValue')
        ->withConsecutive(
             [':id', $id, PDO::PARAM_INT],
             [':ip', $ip, PDO::PARAM_STR]
        );
        
    $pdo = $this->createMock('PDO');
    $pdo->expects($this->once())
        ->method('prepare')
        ->with("INSERT INTO `ip` (`id`, `address` ,`timestamp`)VALUES (:id,:ip,CURRENT_TIMESTAMP)")
        ->willReturn($stmt);
        
    $validData = [
        'id' => $id,
        'ip' => $ip,
        'conn' => $pdo
    ];

    $ipStmt = new ip_request();
    $ipStmt->setTimestampCount(...$validData);
}

【讨论】:

  • 感谢@duncan,这个解释确实帮助澄清了一切,现在它完美运行,感谢您帮助确认不同的部分。
猜你喜欢
  • 2011-07-19
  • 2011-07-17
  • 2015-11-01
  • 2018-05-14
  • 2011-03-09
  • 1970-01-01
  • 1970-01-01
  • 2021-04-09
  • 1970-01-01
相关资源
最近更新 更多