php 在 PHPUnit Mocks 的 returnCallback() 中修改对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4702132/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 14:02:34  来源:igfitidea点击:

Modifying objects in returnCallback() of PHPUnit Mocks

phpphpunit

提问by Alex Lawrence

I want to mock a method of a class and execute a callback which modifies the object given as parameter (using PHP 5.3 with PHPUnit 3.5.5).

我想模拟一个类的方法并执行一个回调来修改作为参数给出的对象(使用 PHP 5.3 和 PHPUnit 3.5.5)。

Let′s say I have the following class:

假设我有以下课程:

class A
{
  function foobar($object) 
  {
    doSomething();
  }
}

And this setup code:

这个设置代码:

$mock = $this->getMockBuilder('A')->getMock();
$mock->expects($this->any())->method('foobar')->will(
  $this->returnCallback(function($object) {
    $object->property = something;
  }));

For some reason the object does not get modified. On var_dumping $objectI see it is the right object. Could it be that the object gets passed by value? How can I configure the mock to receive a reference?

由于某种原因,该对象没有被修改。在var_dumping$object我看到它是正确的对象。难道对象是按值传递的?如何配置模拟以接收参考?

回答by edorian

He Alex,

他亚历克斯,

i talked to Sebastian (the phpunit creator) about that problem and yes: The argument gets cloneed before it is passed to the callback.

我和 Sebastian(phpunit 创建者)讨论了这个问题,是的:参数在clone传递给回调之前被ed。

From the top of my head i can't offer you any workaround but i choose to answer anyway to at least tell you that you are doing nothing wrong and that this is expected behavior.

在我的脑海中,我无法为您提供任何解决方法,但无论如何我选择回答以至少告诉您您没有做错任何事情,这是预期的行为。

To quote Sebastians comment on IRC on why it clones the argument:

引用 Sebastians 对 IRC 的评论,解释为什么它会克隆这个论点:

It's a long-going debate between me, myself, and users of PHPUnit whether or not this is right ;-)

这是我、我自己和 PHPUnit 用户之间的长期争论,这是否正确;-)

To have something copy/pasteable:

要复制/粘贴某些内容:

Assertion 3 in this codesample will fail. (The variable is only changed in the returned object)

此代码示例中的断言 3 将失败。(变量只在返回的对象中改变)

<?php
class A
{
    function foobar($o)
    {
        $o->x = mt_rand(5, 100);
    }
}

class Test extends PHPUnit_Framework_TestCase
{
    public function testFoo()
    {
        $mock = $this->getMock('A');
        $mock->expects($this->any())
             ->method('foobar')
             ->will($this->returnCallback(function($o) { $o->x = 2; return $o; }));

        $o = new StdClass;
        $o->x = 1;

        $this->assertEquals(1, $o->x);
        $return = $mock->foobar($o);

        $this->assertEquals(2, $return->x);
        $this->assertEquals(2, $o->x);
    }
}


Update:

更新:

Starting with PHPUnit 3.7 the cloning can be turned off. See the last argument off:

从 PHPUnit 3.7 开始,可以关闭克隆。查看最后一个参数:

public function getMock(
    $originalClassName, 
    $methods = array(), 
    array $arguments = array(), 
    $mockClassName = '', 
    $callOriginalConstructor = TRUE, 
    $callOriginalClone = TRUE, 
    $callAutoload = TRUE, 
    $cloneArguments = FALSE
);

It might even be off by default :)

它甚至可能默认关闭:)

回答by David Harkness

The class that performs the cloning of the parameters passed to the mocked method is PHPUnit_Framework_MockObject_Invocation_Static. Looking at cloneObject()you can see that it will return the original object if the parameter's class's __clone()method is not public.

执行克隆传递给模拟方法的参数的类是PHPUnit_Framework_MockObject_Invocation_Static. 查看cloneObject()您可以看到,如果参数的类的__clone()方法不是公共的,它将返回原始对象。

If you have control over the class of the parameters objects andyou don't need to ever clone them yourself, you can add a private empty __clone()method.

如果您可以控制参数对象的类并且您不需要自己克隆它们,您可以添加一个私有的空__clone()方法。

回答by iisisrael

This is really old, but it came up at the top of a search and pointed me in the right direction, so worth updating. Since PHPUnit 6.0, use disableArgumentCloning(), like so:

这真的很旧,但它出现在搜索的顶部并为我指明了正确的方向,因此值得更新。从 PHPUnit 6.0 开始,使用disableArgumentCloning(),像这样:

return $this->getMockBuilder('A')
    ->disableOriginalConstructor()
    ->disableArgumentCloning()
    ->setMethods(array('foobar'))
    ->getMock()
;