PHPUnit:如何使用多个参数模拟多个方法调用?

Posted

技术标签:

【中文标题】PHPUnit:如何使用多个参数模拟多个方法调用?【英文标题】:PHPUnit: how do I mock multiple method calls with multiple arguments? 【发布时间】:2012-06-12 19:45:54 【问题描述】:

我正在为使用 phpUnit 的方法编写单元测试。我正在测试的方法在同一个对象上调用相同的方法 3 次,但使用不同的参数集。我的问题类似于here和here提出的问题

其他帖子中提出的问题与只接受一个参数的模拟方法有关。

但是,我的方法需要多个参数,我需要这样的东西:

$mock->expects($this->exactly(3))
->method('MyMockedMethod')
    ->with(
        $this->logicalOr(
            $this->equalTo($arg1, $arg2, arg3....argNb),
            $this->equalTo($arg1b, $arg2b, arg3b....argNb),
            $this->equalTo($arg1c, $arg2c, arg3c....argNc)
        )
    );

此代码不起作用,因为equalTo() 仅验证一个参数。给它多个参数会引发异常:

PHPUnit_Framework_Constraint_IsEqual::__construct() 的参数 #2 必须是数字

有没有办法为具有多个参数的方法进行logicalOr 模拟?

提前致谢。

【问题讨论】:

你还没有提出一个问题(用真实的话)。此外,您还没有解释为什么您专门添加的代码对您不起作用。这听起来可能是多余的,但这些信息将有助于使您的问题更清晰,更容易提供答案。我们无法调查你的大脑。 @Thomas:我是来问这个exact问题的,所以我更新了你的问题并点赞了你的问题,希望你不要介意。 (现在我们玩等待游戏……) @drHannibalLecter 谢谢 【参考方案1】:

就我而言,答案很简单:

$this->expects($this->at(0))
    ->method('write')
    ->with(/* first set of params */);

$this->expects($this->at(1))
    ->method('write')
    ->with(/* second set of params */);

关键是使用$this->at(n)n是该方法的第N次调用。我对我尝试过的任何logicalOr() 变体都无能为力。

【讨论】:

我认为这里的关键是with 接受一系列约束(如果只给出一个值,则暗示equalTo)。您应该可以使用logicalOr,但只能在每个参数的上下文中单独使用(这可能不是很有用)。 @drHannibalLecter 这行得通,但这里的问题是它对您的测试进行硬编码以关心测试用例的内部实现。 @drHannibalLecter 似乎索引从“1”而不是“0”开始。 这里请注意:$this->at() 的索引从零开始,但在模拟对象上的每个 方法调用都会增加。 @GeoffreyBrier,这可能就是为什么它对你来说是索引 1 ...。阅读此处了解更多信息:phpunit.de/manual/current/en/… 不推荐使用 matcher 的快速注释并将在 phpunit 10 中删除,应使用 cody 的答案,包括 withConsecutive【参考方案2】:

对于希望同时匹配输入参数并为多个调用提供返回值的其他人。这对我有用:

    $mock->method('myMockedMethod')
         ->withConsecutive([$argA1, $argA2], [$argB1, $argB2], [$argC1, $argC2])
         ->willReturnOnConsecutiveCalls($retValue1, $retValue2, $retValue3);

【讨论】:

【参考方案3】:

Stubbing a method call to return the value from a map

$map = array(
    array('arg1_1', 'arg2_1', 'arg3_1', 'return_1'),
    array('arg1_2', 'arg2_2', 'arg3_2', 'return_2'),
    array('arg1_3', 'arg2_3', 'arg3_3', 'return_3'),
);
$mock->expects($this->exactly(3))
    ->method('MyMockedMethod')
    ->will($this->returnValueMap($map));

或者你可以使用

$mock->expects($this->exactly(3))
    ->method('MyMockedMethod')
    ->will($this->onConsecutiveCalls('return_1', 'return_2', 'return_3'));

如果您不需要指定输入参数

【讨论】:

你误解了这个问题,这不是关于返回值,而是关于方法的多次调用的不同参数。 @drHannibalLecter 好的,知道了。只是困惑with()/will() 连续调用比手动执行 at() 好得多 onConsecutiveCalls 是我刚才的答案。工作真棒;谢谢!【参考方案4】:

如果有人在没有查看 phpunit 文档中的 correspondent section 的情况下发现此问题,您可以使用 withConsecutive 方法

$mock->expects($this->exactly(3))
     ->method('MyMockedMethod')
     ->withConsecutive(
         [$arg1, $arg2, $arg3....$argNb],
         [arg1b, $arg2b, $arg3b....$argNb],
         [$arg1c, $arg2c, $arg3c....$argNc]
         ...
     );

唯一的缺点是代码必须按照提供的参数顺序调用 MyMockedMethod。我还没有找到解决方法。

【讨论】:

我相信这就是“地图”功能的用途,例如willReturnMap 或 returnValueMap - 就像从参数到返回的查找。

以上是关于PHPUnit:如何使用多个参数模拟多个方法调用?的主要内容,如果未能解决你的问题,请参考以下文章

PHPUnit 中的模拟 - 具有不同参数的同一方法的多个配置

如何通过phpunit对一个方法进行单元测试,该方法具有多个内部调用保护/私有方法?

如何使用 PHPUnit 在类中测试单个 void 方法?

PHPUnit:如何通过测试方法模拟内部调用的函数

phpunit mock 方法多次调用不同参数

用 phpunit 模拟 symfony 安全,返回空值而不是布尔值(不使用预言)