【问题标题】:PhpUnit mocking: function undefinedPhpUnit 模拟:函数未定义
【发布时间】:2013-12-09 05:00:04
【问题描述】:

我正在为我的视图助手编写一些测试。这是我第一次想用模拟对象做一些事情。我正在使用默认的 PHPUnit 模拟框架。

我编写了一个函数来准备我的模拟对象:

private function getTestStub(){
    $mockResult = array();
    $mock =  $this->getMock('My\Entity\Product');
    $mock->expects($this->once())
                ->method('getId')
                ->will($this->returnValue(1));
    $mock->expects($this->once())
        ->method('getName')
        ->will($this->returnValue('jan'));
    $mock->expects($this->once())
        ->method('getWoonplaats')
        ->will($this->returnValue('Amsterdam'));
    $mockResult[] = $mock;
    return $mockResult;
}

现在,当我使用此存根进行测试时,我收到以下错误:

Fatal error: Call to undefined method Mock_Product_129abca6::getId()

我在这里做错了什么?

【问题讨论】:

  • Product::getId 存在吗?这不是魔术方法(例如__call 生成的东西)?
  • 好吧,正如你在我的代码中看到的那样,它被嘲笑了
  • 你应该发布在My\Entity\Product定义的类(或接口)。
  • 但是那个类被嘲笑了。真正的 My\Entity\Product 与它有什么关系?

标签: php mocking phpunit


【解决方案1】:

PHPUnit 通过reflection 或get_class_methods 查看您尝试模拟的类。

您模拟的类(如果存在)由模拟对象扩展。类似地实现接口。您可以在代码本身中see how this works。这是一堆代码生成的东西,如果你想看看并跟踪它是如何工作的,一个好的起点是PHPUnit_Framework_MockObject_Generator::generate。

没有看到你试图模拟的类,我猜你的 getter 是由__call 生成的“神奇”方法,如下所示:

<?php
namespace My\Entity;

class Product
{
    private $data = array();

    public function __call($method, $args)
    {
        $set_or_get = strtolower(substr($method, 0, 3));
        $prop = strtolower(substr($method, 3));
        if ('get' === $set_or_get) {
            return isset($this->data[$prop]) ? $this->data[$prop] : null;
        } elseif ('set' === $set_or_get && isset($args[0])) {
            $this->data[$prop] = $args[0];
        } else {
            throw new \BadMethodCallException();
        }
    }
}

PHPUnit 不能真正做你想做的事,因为你试图调用的方法实际上并不存在,而且神奇的 __call 不能按预期工作。要么是因为 PHPUnit 使用了该方法本身的其他东西(你必须深入挖掘才能找到它)。要解决此问题,您需要tell PHPUnit which methods you want to include in your mock。

// the second argument let's you define methods
$mock =  $this->getMock('My\Entity\Product');
// try doing this instead
$mock =  $this->getMock('My\Entity\Product', array('getId', 'getName', 'getWoonplaats'));

This question 也有上述的一些很好的例子和解决方法。

【讨论】:

  • 嗯,我从 PHPUnit 手册中不明白 Mocking 是这样工作的。因此,如果实际类中不存在 mocket 方法,那么它将不起作用,并且会说该方法未定义。
  • 感谢您的回答,当我不小心嘲笑 'foo\service\foobar` 而不是 foo\entity\foobar 时,它有所帮助。也许 TLDR 可以改进这个答案:如果被模拟的类没有定义该方法,method('foo')-&gt;willReturn('bar') 将不会做任何事情。
猜你喜欢
  • 2017-01-13
  • 2012-05-26
  • 1970-01-01
  • 2016-01-30
  • 2015-10-03
  • 2020-05-08
  • 2015-12-18
  • 2013-02-23
  • 1970-01-01
相关资源
最近更新 更多