且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

检查是否在没有传递任何参数的情况下调用了 mock 的方法(在 phpunit 中)

更新时间:2023-02-20 22:00:26

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

While rdlowrey is correct that with() doesn't make provisions for checking for no arguments passed, the problem doesn't lie with PHPUnit but PHP itself.

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

First, if your method doesn't provide default values, the interpreter will raise a fatal error if you don't pass any parameters. This is expected and not entirely relevant to the question at hand, but it's important to state up front.

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

Second, if your method does provide default values, calling the method without arguments will cause PHP to alter the call before PHPUnit gets involved to pass the defaults instead. Here's a simple test that demonstrates PHP inserting itself before PHP can check the parameters. It's key to realize that the mock class that PHP creates has the same signature as the mocked class--including the defaults.

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

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

This means you can verify that either nothing was passed or the default values were passed, but you cannot be more specific.

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

Can you get around this limitation? You can remove default values from arguments when overriding methods, so you should be able to create a subclass and mock it. Is it worth it? My initial gut reaction is that this is a huge code smell. Either your design or your tests are doing the Wrong Thing(tm).

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

If you can provide a real-world, concrete example where you actually need to do this kind of test, it's worth spending some time pondering a solution. Until then, I'm satisfied with the purely academic answer of "don't do that." :)