【问题标题】:Check that mock's method is called without any parameters passed (in phpunit)检查是否在没有传递任何参数的情况下调用了 mock 的方法(在 phpunit 中)
【发布时间】:2012-03-17 13:21:43
【问题描述】:

在 phpunit 中我们可以指定具体调用的方法

->with($this->equalTo('foobar'))

或任何

->with($this->anything())

参数。

但是有没有办法指定该方法是在没有参数的情况下调用的?

这是我预计会失败的测试:

public function testZ()
{
    $a = $this->getMock('q');

    $a->expects($this->once())
        ->method('z')
        ->with(); // <--- what constraint to specify here?

    $a->z(1);
}

UPD

这个问题具有理论性质,所以我没有任何现实生活中的例子。我现在能想到的一些可能有用的情况是:

public function testMe($object)
{
    $object->foo();
}

让我们假设testMe 应该(根据设计和要求)始终调用不带参数的方法(假设foo() 具有默认参数)。因为在这种情况下,任何非默认参数(更准确地说:任何参数 != 到默认参数,我们还不知道并且可能会独立更改)都会导致致命的后果。

【问题讨论】:

  • +1 我花了几分钟在 github 上浏览 PHPUnit 源代码,似乎 -&gt;with() 仅用于实际指定参数,而不是禁止它们。不过,我真的想不出一种情况,即验证没有传递任何参数是您需要检查的事情?我很想知道您为什么需要验证没有发送任何参数。在没有预期的情况下传递一个参数不会破坏任何东西,当有一个预期无法提供的参数时,你不能只传递一个 NULL 吗?
  • @rdlowrey:添加了解释更新 ;-)
  • @rdlowrey: can't you just pass in a NULL when there is an argument expected that can't be provided --- 不,你不能。方法仍然可以有可选参数
  • 是的......我在阅读您的更新后尝试从我的评论中重新编辑该声明,但 5 分钟窗口已经过去;)我希望我可以 +5 这个问题,因为我我也想知道并且从查看源代码中我找不到在这种情况下有效的约束。您可能需要超出 PHPUnit 内置功能的范围,并使用您自己的模拟类扩展被测类,如果参数被传递给方法,则会引发异常或其他东西......
  • @rdlowrey:我会接受it is impossible 作为有效答案,只要经过一些代码研究我也看不到任何可能性。

标签: php unit-testing testing phpunit


【解决方案1】:

虽然 rdlowrey 是正确的,with() 没有规定检查没有传递的参数,但问题不在于 PHPUnit,而在于 PHP 本身。

首先,如果你的方法没有提供默认值,如果你不传递任何参数,解释器会抛出一个致命错误。这是意料之中的,与手头的问题并不完全相关,但提前说明这一点很重要。

其次,如果你的方法确实提供了默认值,调用不带参数的方法将导致 PHP 在 PHPUnit 参与传递默认值之前改变调用。这是一个简单的测试,演示 PHP 在 PHP 可以检查参数之前插入自身。认识到 PHP 创建的模拟类与模拟类具有相同的签名是关键——包括默认值。

class MockTest extends PHPUnit_Framework_TestCase {
        public function test() {
                $mock = $this->getMock('Foo', array('bar'));
                $mock->expects($this->once())
                     ->method('bar')
                     ->with()    // does nothing, but it doesn't matter
                     ->will($this->returnArgument(0));
                self::assertEquals('foobar', $mock->bar());  // PHP inserts 1 and 2
                // assertion fails because 1 != 'foobar'
        }
}

class Foo {
        public function bar($x = 1, $y = 2) {
                return $x + $y;
        }
}

这意味着您可以验证要么没有传递任何内容或传递了默认值,但你不能更具体。

你能绕过这个限制吗?您可以在覆盖方法时从参数中删除默认值,因此您应该能够创建一个子类并模拟它。这值得么?我最初的直觉反应是,这是一种巨大的代码气味。您的设计或测试都在做错事(tm)。

如果您可以提供一个实际需要进行此类测试的具体示例,那么值得花一些时间思考解决方案。在那之前,我对“不要那样做”的纯学术回答很满意。 :)

【讨论】:

  • +1 进行澄清。我同意这似乎是一种代码味道......我还没有看到它的实际应用程序,这似乎是一个简单的重构可以避免的问题。
  • “在此之前,我对“不要那样做”的纯学术回答感到满意。:)“---我同意我想不出现实世界的例子,那就是为什么我说“这个问题具有理论性质”-所以我只是询问了理论上这样做的可能性,而没有解释为什么我需要这样做;-)
  • @zerkms - 听起来是个很棒的新问题。 :)
  • @David Harkness:不。所有对答案感兴趣的人都有能力浏览其来源:-)
【解决方案2】:

PHPUnit 模拟对象只能使用-&gt;with() 约束方法来验证传递参数的计数或相应值是否与调用时传递给模拟方法的那些相匹配。您不能使用它来要求 没有参数 传递给模拟方法。

模拟验证过程专门检查传递的参数计数和相关值是否与传递给with 约束的值兼容。此外,正如您可能已经看到的那样,指定不带任何值的 with 约束也不起作用;如果with 没有接收到参数,则不会添加任何参数约束进行验证。

您可以在链接的 github 源中看到用于验证模拟方法参数的实际 PHPUnit_Framework_MockObject_Matcher_Parameters::verify 方法。

如果需要验证未传递的参数,则需要指定自己的模拟类来验证 PHPUnit 模拟功能之外的此类条件。

【讨论】:

  • 在不编辑此答案的情况下,我想补充一点,我得出这个结论的原因不是对 PHPUnit 内部结构的细微差别理解,而是长时间搜索相关源代码以尝试完成问题的既定目标。我相信链接的源代码证明它是不可能的,但如果有人能想办法让它工作,请分享。
猜你喜欢
  • 1970-01-01
  • 2021-11-10
  • 2020-01-11
  • 2012-05-28
  • 1970-01-01
  • 2017-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多