Изменение объектов в returnCallback () из PHPUnit Mocks - PullRequest
19 голосов
/ 16 января 2011

Я хочу смоделировать метод класса и выполнить обратный вызов, который изменяет объект, заданный в качестве параметра (используя 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 Я вижу, что это правильный объект. Может ли быть так, что объект передается по значению? Как я могу настроить макет для получения ссылки?

Ответы [ 3 ]

42 голосов
/ 17 января 2011

Он Алекс,

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

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

Цитируя Себастьян, прокомментируйте IRC, почемуон клонирует аргумент:

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

Иметь что-тоcopy / pasteable:

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

Обновление:

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

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

Возможно, он даже отключен по умолчанию:)

5 голосов
/ 26 января 2011

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

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

0 голосов
/ 26 октября 2018

Это действительно старая версия, но она оказалась в начале поиска и указала мне верное направление, поэтому стоит обновить. Начиная с PHPUnit 6.0, используйте disableArgumentCloning(), например:

return $this->getMockBuilder('A')
    ->disableOriginalConstructor()
    ->disableArgumentCloning()
    ->setMethods(array('foobar'))
    ->getMock()
;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...