Эквивалент SimpleTest "частичные насмешки" в PHPUnit?
Я пытаюсь перенести кучу тестов из SimpleTest в PHPUnit, и мне было интересно, есть ли эквивалент для частичных проверок SimpleTest.
Обновление: я не могу найти ничего в документах, которые предполагают, что эта функция доступна, но мне пришло в голову, что я мог бы просто использовать подкласс. Это хорошая или плохая идея?
class StuffDoer {
protected function doesLongRunningThing() {
sleep(10);
return "stuff";
}
public function doStuff() {
return $this->doesLongRunningThing();
}
}
class StuffDoerTest {
protected function doesLongRunningThing() {
return "test stuff";
}
}
class StuffDoerTestCase extends PHPUnit_Framework_TestCase {
public function testStuffDoer() {
$sd = new StuffDoerTest();
$result = $sd->doStuff();
$this->assertEquals($result, "test stuff");
}
}
5 ответов
При прочтении связанной страницы частичная имитация SimpleTest кажется ложной, где только некоторые методы переопределяются. Если это правильно, эта функциональность обрабатывается обычным макетом PHPUnit.
Внутри PHPUnit_Framework_TestCase
создаешь макет с
$mock = $this->getMock('Class_To_Mock');
Который создает фиктивный экземпляр, где все методы ничего не делают и возвращают ноль. Если вы хотите переопределить только некоторые методы, второй параметр getMock
это массив методов для переопределения.
$mock = $this->getMock('Class_To_Mock', array('insert', 'update'));
создаст фиктивный экземпляр Class_To_Mock
с insert
а также update
функции удалены, готовы к указанию возвращаемых значений.
Эта информация находится в документации phpunit.
Обратите внимание: в этом ответе приведены более свежие примеры кода для версий PHPUnit, начиная с версии 5.4.
PHPUnit_Framework_TestCase::getMock
устарела с phpunit 5.4. Мы можем использовать setMethods
вместо.
setMethods (array $ методов) можно вызвать в объекте Mock Builder, чтобы указать методы, которые должны быть заменены настраиваемым двойным тестом. Поведение других методов не изменилось. Если вы вызываете setMethods(null), то никакие методы не будут заменены.
https://phpunit.de/manual/current/en/test-doubles.html
$observer = $this->getMockBuilder(Observer::class)
->setMethods(['update'])
->getMock();
Обратите внимание, что выше getMock
является PHPUnit_Framework_MockObject_MockBuilder::getMock
, (Phpunit5.6)
Методы
setMethods
устарели. Теперь работает:
$mock = $this->getMockBuilder(ClassToMock::class)
->onlyMethods(['insert', 'update'])
->getMock();
Я не думаю, что PHPUnit поддерживает частичные проверки для тестируемой системы. Если вы пытаетесь изолировать методы, то я уверен, что ваша реализация работает - я тоже это сделал.
Однако я стараюсь избегать этого по нескольким причинам.
Во-первых, он очень тесно связывает ваш тест с внутренней реализацией класса. Вас действительно волнует, называется ли метод doesLongRunningThing
был вызван, или это более важно, что "LongRunningThing" был сделан?
Во-вторых, когда я сталкиваюсь с этим, это всегда заставляет меня задаться вопросом, есть ли у меня один класс, выполняющий работу двух. Рефакторинг класса экстракта может быть в порядке. Тестирование становится намного проще, если doesLongRunningThing()
становится своим собственным классом, даже с одним методом.
Я считаю, что решение состоит в том, чтобы внедрить сервисы, от которых зависит ваше SUT (http://en.wikipedia.org/wiki/Dependency_injection). Это также делает DoesLongRunningThing
реализация более тестируема.
Не вдаваясь в интерфейсы, вот что я бы сделал:
class DoesLongRunningThing {
public function execute() {
sleep(10);
return "stuff";
}
}
class StuffDoer {
protected $doesLongRunningThing;
public function setLongRunningThinger(DoesLongRunningThing $obj) {
$this->doesLongRunningThing = $obj;
}
public function doStuff() {
return $this->doesLongRunningThing->execute();
}
}
Теперь легко издеваться
class StuffDoerTestCase extends PHPUnit_Framework_TestCase {
public function testStuffDoer() {
$dlrtMock = $this->getMock('DoesLongRunningThing');
$dlrtMock->expects($this->any())->will($this->returnValue("test stuff"));
$sd = new StuffDoer();
$sd->setLongRunningThinger($dlrtMock);
$result = $sd->doStuff();
$this->assertEquals($result, "test stuff");
}
}
Возможно, класс, который вы издеваетесь, не входит в область действия (у меня была эта проблема). Когда решение было решено, я смог издеваться над одной функцией и проверить фактическую логику другой.
$mockController = $this->getMockBuilder('ControllerClassName')
->setMethods(['functionToMock'])
->getMock();
$result = $mockController->anotherFunction();
$this->assertEquals(true, $result);