简体   繁体   English

如何在 SQL INSERT 语句上获得与 PHPUnit 一起使用的模拟和 PDO

[英]How to get a mock & PDO working with PHPUnit on a SQL INSERT statement

I have a test class, called ip_requestTest.php which has the following function:我有一个名为ip_requestTest.php的测试 class,它具有以下 function:

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);

    }

Now the last line is the problem, $ipStmt->setTimestampCount($validData);现在最后一行是问题所在, $ipStmt->setTimestampCount($validData);

My actual setTimestampCount() function doesn't take just one parameter.我的实际 setTimestampCount() function 不只采用一个参数。

It looks like this:它看起来像这样:

    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();

    }

Can anyone help, or give me any clues as to how I can have this function being tested properly?谁能帮忙,或者给我任何线索,让我知道如何正确测试这个 function?

How do I include the data into the testing function call?如何将数据包含到测试 function 调用中?

Thanks谢谢

UPDATE 1:更新 1:

Tried the suggestion of the splat operator, and also removed timestamp insertion, as this can be done automatically by inserting the data.尝试了 splat 运算符的建议,还删除了时间戳插入,因为这可以通过插入数据自动完成。

Still not working, the error i get is Too few arguments to function 2 passed, exactly 3 expected.仍然无法正常工作,我得到的错误是Too few arguments to function 2 passed, exactly 3 expected. I know what this menas obviously, but how does the splat operator deal with this?我显然知道这意味着什么,但是 splat 操作员如何处理这个问题? if at all?如果有的话?

Your method setTimestampCount($ip,$id,$conn) expects those 3 parameters.您的方法setTimestampCount($ip,$id,$conn)需要这 3 个参数。

The 3 you're passing in are the contents of $validData , ie您传入的 3 是$validData的内容,即

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

Instead you need to get rid of the timestamp argument from that array, and replace it with your mocked connection.相反,您需要从该数组中删除时间戳参数,并将其替换为您的模拟连接。

Secondly, you're doing this:其次,你正在这样做:

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

However the execute method takes no arguments:但是execute方法不需要 arguments:

$query->execute();

It also doesn't return anything (or at least you're not assigning anything it returns to a variable), so there's no need to do ->willReturn(true);它也不返回任何东西(或者至少你没有将它返回的任何东西分配给变量),所以没有必要做->willReturn(true);

So the above should just be:所以上面应该只是:

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

There's also no point testing the timestamp.测试时间戳也没有意义。 All you should be doing is checking the string you're passing into the prepare method.您应该做的就是检查您传递给prepare方法的字符串。 So you need to check two strings look identical.所以你需要检查两个字符串看起来是否相同。 Currently the string you're testing is:目前您正在测试的字符串是:

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

And you're specifying你正在指定

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

So things like the missing ( , ) , the difference in spacing between arguments etc, will all cause this test to fail.因此,缺少( , ) 、 arguments 之间的间距差异等都会导致此测试失败。 They need to be identical.它们必须相同。

Finally, you're not actually testing whatever the ip and id or timestamp values are.最后,您实际上并没有测试ipid或时间戳值是什么。 The prepare statement doesn't actually use them; prepare语句实际上并没有使用它们。 it just has the string :id,:ip,CURRENT_TIMESTAMP .它只有字符串:id,:ip,CURRENT_TIMESTAMP

You should also really test the bindValue method, which does use the ip and id values.您还应该真正测试bindValue方法,该方法确实使用 ip 和 id 值。 That's slightly more complicated by the fact you're calling it twice, however you can use PHPUnit's withConsecutive method to handle this:由于您要调用它两次,这会稍微复杂一些,但是您可以使用PHPUnit 的withConsecutive方法来处理它:

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

So I'd say the method should look like:所以我想说这个方法应该是这样的:

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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM