PHPUnit: фиктивные методы существующего объекта - PullRequest
17 голосов
/ 17 августа 2011

PHPUnit's getMock($classname, $mockmethods) создает новый объект на основе заданного имени класса и позволяет мне изменять / проверять поведение указанных мной методов.

Я жажду чего-то другого; это меняет поведение методов существующего объекта - без создания нового объекта.

Это возможно? Если да, то как?


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


Хотя, безусловно, было бы возможно снова создать объект, который нужно смоделировать, это слишком сложно сделать в моем тесте. Таким образом, я не хочу этого делать, если мне действительно не нужно. Это экземпляр TSFE TYPO3, и настроить его один раз в процессе начальной загрузки уже достаточно сложно.

Ответы [ 3 ]

10 голосов
/ 08 июля 2014

Я знаю, что этот ответ довольно поздно, но я чувствую, что для будущих зрителей этого вопроса теперь существует более простое решение (которое имеет некоторые недостатки, но в зависимости от ваших потребностей может быть гораздо проще реализовать). Mockery имеет поддержку для имитации уже существующих объектов с помощью того, что они называют "прокси частичным макетом." случай (хотя в документах действительно указывается, что это должно быть «последнее средство»).

$existingObjectMock = \Mockery::mock($existingObject);
$existingObjectMock->shouldReceive('someAction')->andReturn('foobar');

Он действует, создавая прокси-объект, который передает все вызовы методов, и атрибут получает / устанавливает существующий объект, если только они не являются имитированными.

Следует отметить, однако, что прокси страдает от очевидной проблемы неудачи любых проверок типов или подсказок. Но этого обычно можно избежать, потому что $existingObject все еще можно обойти. Вы должны использовать $existingObjectMock только тогда, когда вам нужны фиктивные возможности.

10 голосов
/ 17 августа 2011

Позвольте мне начать со слов: добро пожаловать на темную сторону модульного тестирования.

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

runkit

То, что вы можете сделать довольно легко, не так просто, как изменение архитектуры вашего приложения, - это изменить поведение класса на лету с помощью runkit

runkit_method_rename(
    get_class($object), 'methodToMock', 'methodToMock_old'
);
runkit_method_add(
    get_class($object), 'methodToMock', '$param1, $param2', 'return 7;'
);

runkit :: method_add

и после теста к method_remove и переименованию снова.Я не знаю ни одного фреймворка / компонента, который бы вам в этом помог, но это не так уж много для самостоятельной реализации в UglyTestsBaseTest extends PHPUnit_Framework_TestCase.

Ну ...

Если всеиметь доступ к является ссылкой на этот объект (как в: $x в $x = new Foo();), я не знаю, как сказать: $ x, вы сейчас типа SomethingElse и все другие переменные, указывающие наэтот объект тоже должен измениться.

Я предполагаю, что вы уже знаете такие вещи, как тестирование ваших рядовых , но это не поможет вам, потому что вы не можете контролировать жизненный цикл объектов .

Расширение помощников по тестированию php

Примечание: расширение Test-Helper заменено наhttps://github.com/krakjoe/uopz

Здесь вам также могут помочь: Использование жестко закодированных зависимостей с использованием расширения php-test-helpers , что позволяет вам делать такие вещи, как Перехват создания объектов .

Это означает, что когда ваше приложение вызывает$x = new Something(); вы можете взломать PHP, чтобы сделать так, чтобы $x содержал экземпляр YourSpecialCraftedSomething.

. Вы можете создать эту классификацию, используя PHPUnit Mocking API, или написать ее самостоятельно.


Насколько я знаю, это ваши варианты.Если вам стоит пойти туда (или просто написать тесты на интеграцию / селен для этого проекта), вы должны сами в этом разобраться, так как это сильно зависит от ваших обстоятельств.

6 голосов
/ 22 мая 2012

Не весь ранее существующий код может быть протестирован.Код действительно должен быть разработан для тестированияТаким образом, хотя это не совсем то, что вы просите, вы можете выполнить рефакторинг кода, чтобы создание экземпляра объекта выполнялось в отдельном методе, а затем смоделировать этот метод для возврата того, что вы хотите.

class foo {
  function new_bar($arg) { return new bar($arg); }
  function blah() {
    ...
    $obj = $this->new_bar($arg);
    return $obj->twiddle();
  }
}

итогда вы можете проверить это с

class foo_Test extends PHPUnit_Framework_TestCase {
  function test_blah() {
    $sut = $this->getMock('foo', array('new_bar', 'twiddle'));
    $sut->expects($this->once())->method('new_bar')->will($this->returnSelf());
    $sut->expects($this->once())->method('twiddle')->will($this->returnValue('yes'));
    $this->assertEquals('yes', $sut->blah());
  }
}
...