PHPUnit 调用未定义的方法 'Mock_x_::method()'


PHPUnit call to undefined method `Mock_x_::method()`

我正在尝试创建我的第一个phpunit测试,发现自己需要在IMailer接口上存根一个方法。

interface IMailer
{
    public function send($to, $from, $cc, $subject, $body);
    public function sent();
}
    $mailer = $this->getMockBuilder(
        'IMailer',
        array('send', 'sent'))->getMock();
    $mailer->method('send')->willRreturn(0);

但是,我不断得到

PHP Fatal error: 
  Call to undefined method Mock_Mailer_13fc0a04::method()
  in ...Test.php on line 16

a var_dump($mailer);结果

class Mock_IMailer_4c3e02a7#215 (1) {
  private $__phpunit_invocationMocker =>
  NULL
}

使用 expect($this->any()) 会给出一个 dito 错误 - 似乎模拟对象没有任何模拟功能......

我正在 ubuntu 盒子上运行 phpunit 3.7.28 和 php 5.5.9。

怎么来了? 我该如何解决它?

getMockBuilder函数只接受className作为参数。初始化模拟对象方法的正确方法是使用 setMethods 函数(参见 phpunit 文档)

   $mailer = $this->getMockBuilder('IMailer')
       ->setMethods(array('send', 'sent'))
       ->getMock();
此外,当您

使用模拟对象时,您可能还希望有一些期望的定义:

   $mailer->expects($this->any())
        ->method('send')
        ->willReturn(0);

编辑

上述情况适用于较新的phpunit版本。对于 phpunit 3.7.28,模拟对象的用法略有不同(即期望似乎是强制性的,并且 willReturn 尚不可用)。对于 3.7.28 版本,您应该将第二部分修改为:

   $mailer->expects($this->any())
        ->method('send')
        ->will($this->returnValue(0));

我建议更新到以后的phpunit版本,因为似乎很难找到这个旧版本的文档。

对于仍在使用旧版本的 PHPUnit,但仍希望能够直接调用method()的任何人,另一种解决方案是覆盖默认的模拟对象类模板。

复制MockObject/Generator/mocked_class.tpl.dist,并将副本命名为mocked_class.tpl 。然后,只需将 method() 方法的代码添加到模板中:

public function method()
{
    $any = new PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount;
    $expects = $this->expects($any);
    $args = func_get_args();
    return call_user_func_array(array($expects, 'method'), $args);
}

这将允许您直接呼叫$mock->method()。但是,您仍然需要使用->will($this->returnValue(0))而不是->willReturn(0)。为此,您需要引入自定义调用生成器和调用模拟器:

class My_MockObject_Builder_InvocationMocker
    extends PHPUnit_Framework_MockObject_Builder_InvocationMocker {
    public function willReturn( $value ) {
        return $this->will( new PHPUnit_Framework_MockObject_Stub_Return( $value ) );
    }
}
class My_MockObject_InvocationMocker
    extends PHPUnit_Framework_MockObject_InvocationMocker {
    public function expects( PHPUnit_Framework_MockObject_Matcher_Invocation $matcher ) {
        return new My_MockObject_Builder_InvocationMocker($this, $matcher);
    }
}

并再次更新您的模板,以使用 My_MockObject_InvocationMocker 而不是 PHPUnit_Framework_MockObject_InvocationMocker .

然后,完整模板将如下所示:

{prologue}{class_declaration}
{
    protected static $staticInvocationMocker;
    protected $invocationMocker;
{clone}{mocked_methods}
    public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher)
    {
        return $this->__phpunit_getInvocationMocker()->expects($matcher);
    }
    public function method()
    {
        $any = new PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount;
        $expects = $this->expects($any);
        $args = func_get_args();
        return call_user_func_array(array($expects, 'method'), $args );
    }
    public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher)
    {
        return self::__phpunit_getStaticInvocationMocker()->expects($matcher);
    }
    public function __phpunit_getInvocationMocker()
    {
        if ($this->invocationMocker === NULL) {
            $this->invocationMocker = new My_MockObject_InvocationMocker;
        }
        return $this->invocationMocker;
    }
    public static function __phpunit_getStaticInvocationMocker()
    {
        if (self::$staticInvocationMocker === NULL) {
            self::$staticInvocationMocker = new My_MockObject_InvocationMocker;
        }
        return self::$staticInvocationMocker;
    }
    public function __phpunit_hasMatchers()
    {
        return self::__phpunit_getStaticInvocationMocker()->hasMatchers() ||
               $this->__phpunit_getInvocationMocker()->hasMatchers();
    }
    public function __phpunit_verify()
    {
        self::__phpunit_getStaticInvocationMocker()->verify();
        $this->__phpunit_getInvocationMocker()->verify();
    }
    public function __phpunit_cleanup()
    {
        self::$staticInvocationMocker = NULL;
        $this->invocationMocker       = NULL;
    }
}{epilogue}