PHPUnit:模拟__get()会导致"__get() must take exactly 1 argument ..."



我在模拟重载的__get($index(方法时遇到了一个问题。要模拟的类和使用它的测试系统的代码如下:

<?php
class ToBeMocked
{
    protected $vars = array();
    public function __get($index)
    {
        if (isset($this->vars[$index])) {
            return $this->vars[$index];
        } else {
            return NULL;
        }
    }
}
class SUTclass
{
    protected $mocky;
    public function __construct(ToBeMocked $mocky)
    {
        $this->mocky = $mocky;
    }
    public function getSnack()
    {
        return $this->mocky->snack;
    }
}

测试如下:

<?php    
class GetSnackTest extends PHPUnit_Framework_TestCase
{
    protected $stub;
    protected $sut;
    public function setUp()
    {
       $mock = $this->getMockBuilder('ToBeMocked')
                     ->setMethods(array('__get')
                     ->getMock();
       $sut = new SUTclass($mock);
    }
    /**
     * @test
     */
    public function shouldReturnSnickers()
    {
        $this->mock->expects($this->once())
                   ->method('__get')
                   ->will($this->returnValue('snickers');
        $this->assertEquals('snickers', $this->sut->getSnack());
    }
}

真正的代码稍微复杂一点,尽管不算太复杂,因为它的父类中有"getSnacks(("。但这个例子就足够了。

问题是,当使用PHPUnit执行测试时,我会收到以下错误:

Fatal error: Method Mock_ToBeMocked_12345672f::__get() must take exactly 1 argument in /usr/share/php/PHPUnit/Framework/MockObject/Generator.php(231)

当我调试时,我甚至无法达到测试方法。它似乎在设置模拟对象时失败了。

有什么想法吗?

__get()接受一个参数,因此您需要为mock提供一个:

/**
 * @test
 */
public function shouldReturnSnickers()
{
    $this->mock->expects($this->once())
               ->method('__get')
               ->with($this->equalTo('snack'))
               ->will($this->returnValue('snickers'));
    $this->assertEquals('snickers', $this->sut->getSnack());
}

with()方法为PHPUnit中的模拟方法设置参数。您可以在测试双打部分找到更多详细信息。

这有点隐藏在评论中,但@dfmuir的回答让我走上了正轨。如果使用回调,模拟__get方法是直接的。

$mock
    ->method('__get')
    ->willReturnCallback(function ($propertyName) {
        switch($propertyName) {
            case 'id':
                return 123123123123;
            case 'name':
                return 'Bob';
            case 'email':
                return 'bob@bob.com';
        }
    }
);
$this->assertEquals('bob@bob.com', $mock->email);

看看模拟的魔术方法__get。可能您从另一个未正确模拟的对象中调用了另一个__get方法。

您在GetSnackTest类的setUp方法中所做的操作不正确。如果你想执行__get方法的代码(我想这将是你测试的重点>(,你必须改变在setup方法中调用setMethods的方式。以下是完整的解释,但以下是相关部分:

传递包含方法名的数组

您已确定的方法:

都是存根,默认情况下全部返回null,容易被覆盖

因此,您需要通过传递null或通过传递一个数组来调用setMethods,该数组包含一些方法(真正想要存根的方法(,但不是-__get(因为您实际上希望执行该方法的代码(。

shouldReturnSnickers方法中,您只需要调用$this->assertEquals('snickers', $this->sut->getSnack());,而不需要前面的带有expect部分的行。这将确保__get方法的代码得到实际执行和测试。

使用AnyParameters((方法可以帮助您,这是正确的:

$this->mock -> expects($this -> once())  
    -> method('__get') -> withAnyParameters()
    -> will($this -> returnValue('snikers'));

相关内容

最新更新