【问题标题】:PHPunit expectation on Mock object is not fulfilled未满足 Mock 对象的 PHPunit 期望
【发布时间】:2015-06-07 17:19:39
【问题描述】:

这是我的课:

public function __construct(Manager $moduleManager, Source\Yesno $yesNo)
{
    $this->moduleManager = $moduleManager;
    $this->yesNo = $yesNo;
}

public function my1()
{
    $this->moduleManager->isOutputEnabled('');
    $this->yesNo->toOptionArray();
}

public function my2()
{
    $this->moduleManager->isOutputEnabled('');
    $this->yesNo->toOptionArray();
}

这是我的测试:

...
    $this->observerMock = $this->getMock(
        'path\to\Observer',
        null,
        [$this->moduleManagerMock, $this->yesNoMock],
        '',
        true
    );
...
public function testMy1()
{
    $this->moduleManagerMock->expects($this->exactly(2))->method('isOutputEnabled');
    $this->yesNoMock->expects($this->exactly(2))->method('toOptionsArray');
    $this->observerMock->my1();
    $this->observerMock->my2();
}

测试返回:

方法名称的期望失败等于 当被调用 2 次时。方法预计会被调用 2 次, 实际调用了 0 次。

我的问题是:我遇到过几次这样的事情,但每次我都无法理解发生了什么。为什么第一个期望是正确的,第二个是不正确的?

更新.1

我忘了说我遇到过几次这样的情况。 这是我注意到的。使用 xDebug 我在测试中看到了这一点

[this]
    [moduleManager] => ModuleManager_Mock_Name_<hash #1>
    [yesNo] => YesNo_Mock_Name_<hash #2>
    [observerObject] =>
        [moduleManager] => ModuleManager_Mock_Name_<hash #1>
        [yesNo] => YesNo_Mock_Name_<hash #3> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

moduleManager 对象在 unittest 对象和观察者对象内部具有相同的缓存。如果我在 moduleMatcher 上应用 smth - 它出现在两个地方

$unittest 的哈希值->yesNo 与 $observerObject 哈希值不同。如果我为某些方法设置匹配器 - 它仅适用于 unittest 类!

为什么会这样?如何防止创建不同的对象

发现更新.2!

当你通过对象管理器创建对象时

    $this->observerMock = $objectManager->getObject(
        'Observer',
        [
            'moduleManager' => $this->moduleManagerMock,
            'yesNo' => $this->yesNoMock,
        ]
    );

变量 'moduleManager' 和 'yesNo' 应该与构造函数中的变量相同:

public function __construct(Manager $moduleManager, Source\Yesno $yesNo)
{
    $this->moduleManager = $moduleManager;
    $this->yesNo = $yesNo;
}

这里是 phpunit 检查的代码:

    foreach ($method->getParameters() as $parameter) {
        $parameterName = $parameter->getName();
        $argClassName = null;
        $defaultValue = null;

        if (array_key_exists($parameterName, $arguments)) {
            $constructArguments[$parameterName] = $arguments[$parameterName];
            continue;
        }

【问题讨论】:

    标签: php mocking phpunit expectations


    【解决方案1】:

    通常,您不应模拟/存根您的被测系统本身。因为在您的测试用例中,$this-&gt;observerMock 对象本身就是一个存根对象(它模仿另一个类的接口,但没有提供任何实现)。

    这意味着m1m2 方法也是模拟方法,它们在被调用时不会做任何事情。随后,您的依赖项(moduleManagerMockyesNoMock)上的模拟方法将永远不会被调用,这就是您的期望失败的原因。

    要正确测试您想要的行为,请直接使用您的 Observer 类:

    public function setUp() {
        $this->moduleManagerMock = $this->getMock(/*...*/);
        $this->yesNoMock = $this->getMock(/*...*/);
    
        // Do not generate a mock object of "Observer", but use the class 
        // under test itself!
        $this->observer = new Observer(
            $this->moduleManagerMock,
            $this->yesNoMock
        );
    }
    
    public function testM1() {
        $this->moduleManagerMock->expects($this->exactly(2))
                                ->method('isOutputEnabled');
        $this->yesNoMock->expects($this->exactly(2))
                        ->method('toOptionsArray');
        $this->observer->my1();
        $this->observer->my2();
    }
    

    【讨论】:

    • 感谢您的回复。我以前也试过你的方法,现在又试了一次。不幸的是,我得到了相同的结果。一些神奇的事情正在发生。
    猜你喜欢
    • 1970-01-01
    • 2016-05-27
    • 2017-04-02
    • 2010-12-10
    • 2012-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-14
    相关资源
    最近更新 更多