Подтвердить что ты не робот

Изменение объектов в returnCallback() PHPUnit Mocks

Я хочу высмеять метод класса и выполнить обратный вызов, который изменяет объект, указанный как параметр (используя PHP 5.3 с PHPUnit 3.5.5).

Предположим, что у меня есть следующий класс:

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

И этот код установки:

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

По какой-то причине объект не изменяется. На var_dump ing $object я вижу, что это правильный объект. Может быть, объект передается по значению? Как настроить макет для получения ссылки?

4b9b3361

Ответ 1

Он Алекс,

я поговорил с Себастьяном (создателем phpunit) об этой проблеме и да: аргумент получает clone ed до того, как он будет передан в обратный вызов.

С головы до ног я не могу предложить вам какое-либо обходное решение, но я все равно хочу ответить, чтобы хотя бы сказать вам, что вы не делаете ничего плохого и что это ожидаемое поведение.

Чтобы процитировать Sebastians, комментарий IRC о том, почему он клонирует аргумент:

Это долгая дискуссия между мной, мной и пользователями PHPUnit, правильно ли это или нет: -)

Чтобы что-то скопировать/вставить:

Утверждение 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:

Начиная с PHPUnit 3.7, клонирование можно отключить. Отключить последний аргумент:

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

Он может даже отключиться по умолчанию:)

Ответ 2

Класс, выполняющий клонирование параметров, переданных в издеваемый метод, PHPUnit_Framework_MockObject_Invocation_Static. Если посмотреть на cloneObject(), вы увидите, что он вернет исходный объект, если метод класса __clone() не является общедоступным.

Если у вас есть контроль над классом объектов параметров, и вам не нужно их клонировать самостоятельно, вы можете добавить частный пустой метод __clone().