Mockery — это фальшивый объектный фреймворк (точнее Test Double framework) от @padraicb , независимый от тестовых фреймворков, таких как PHPUnit. Его можно использовать для быстрой подготовки Mocks, Stubs и других Test Double для использования внутри ваших юнит-тестов.
Я пробовал Mockery с помощью установки PEAR, и должен сказать, что его выразительность выше, чем у системы насмешки PHPUnit. Тем не менее, он может быть слишком мощным для эффективного использования.
Первый взгляд
Документация для Mockery содержится в файле README. Официальная документация не великолепна и для PHPUnit, но есть много неофициальных руководств (включая практические серии), ориентированных на PHPUnit в качестве стандарта.
Однако Mockery не полностью заменяет PHPUnit, так как это всего лишь фальшивый фреймворк; если вам не подходит генерация макетов, заглушек и других дубликатов тестов с помощью PHPUnit, естественным выбором будет принятие более мощного механизма имитации, чем связанного.
У насмешек нет никаких других зависимостей, кроме сильно рекомендованного Hamcrest, который также можно установить через PEAR. Эта версия Hamcrest является портом соответствия Hamcrest для языка PHP. Насмешливый Api в основном статичен, но и PHPUnit находится под прикрытием. Я не сталкивался с проблемами, вытекающими из этого процедурного подхода.
Уникальные черты
Издевательство может создавать Test Doubles для конкретных классов, абстрактных классов или интерфейсов. В частности, он имеет некоторые функции, которых нет в PHPUnit, и это делает его интересным:
- альтернативные ожидания, которые применяются только при использовании определенного набора параметров. Это позволяет создавать умные объекты, которые возвращают разные результаты при разных вызовах.
- запись ожидаемых звонков на макет вместо определения их через API.
- издевательство над несуществующими методами , которое находится на границе потенциально вредных функций. Тем не менее, он может служить для разработки с макетом до извлечения интерфейса. Интерфейс, который не существует, не должен синхронизироваться с тестами во время этих трудных начальных этапов, а должен создаваться только перед следующей фиксацией.
Еще один момент для Mockery заключается в том, что он не пытается быть слишком умным: он не клонирует ожидаемые параметры, как PHPUnit, что позволяет вам проверять их идентичность с помощью ===.
От великой силы приходит великая ответственность
В некоторых аспектах издевательство слишком жесткое для меня: оно может высмеивать статические методы и публичные свойства или перехватывать функции автозагрузки для насмешки над новыми операторами. Я не уверен, что эти функции полезны, кроме тестирования унаследованного кода, и они могут позволить вам дольше следовать проблемному дизайну.
Например, рассмотрим насмешливые цепочки Деметры, в этом примере взятые из документации:
$mock = \Mockery::mock('CaptainsConsole'); $mock->shouldReceive('foo->bar->zebra->alpha->selfDestruct')->andReturn('Ten!');
Производственный код теперь может вызывать $ object-> foo () -> bar () -> zebra () -> alpha () -> selfDestruct (). Тем не менее, код, который предполагает так много о графе окружающих объектов, очень связан с другими классами и будет ломаться всякий раз, когда его окружение изменяется; в этом случае мощь инфраструктуры тестирования препятствует обратной связи, которую могут дать тесты: избегайте этой цепочки вызовов.
В этом смысле Мокери чувствует себя стартапом, который продает машину, которая может очень быстро заводиться, чтобы грабители могли избежать полиции. ?
Покажи мне код
Api of Mockery — это волшебство: в качестве первого аргумента mock () можно передать все, например, имя, имя класса или массив с фиксированным ожиданием. То же самое относится к mustReceive (), который принимает цепочки даже Demeter, как вы видели.
Снова в Api мы находим мощные функции, такие как частичные имитации, реализованные в виде прокси для реального объекта. Вот пример того, что я нашел наиболее полезным с точки зрения объектно-ориентированного дизайна:
<?php use \Mockery as m; class MockeryExploratoryTest extends PHPUnit_Framework_TestCase { public function testMockASimpleMethodCalledMultipleTimes() { $service = m::mock('PlanetColorService'); $service->shouldReceive('getColor')->times(2)->andReturn('blue', 'red'); $this->assertEquals('blue', $service->getColor('Earth')); $this->assertEquals('red', $service->getColor('Mars')); } public function testMockAnInterface() { $service = m::mock('ColorService'); $service->shouldReceive('getColor')->andReturn('blue'); $this->assertEquals('blue', $service->getColor('water')); } /** * @expectedException InvalidArgumentException */ public function testClassicThrowingOfAnException() { $service = m::mock('PlanetColorService'); $service->shouldReceive('getColor')->withAnyArgs()->andThrow(new InvalidArgumentException); $service->getColor('Earth'); } /** * PHPUnit cannot do this. */ public function testMockWithDifferentReturnValuesForDifferentExpectations() { $service = m::mock('PlanetColorService'); $service->shouldReceive('getColor')->with('Earth')->andReturn('blue'); $service->shouldReceive('getColor')->with('Mars')->andReturn('red'); $this->assertEquals('blue', $service->getColor('Earth')); $this->assertEquals('red', $service->getColor('Mars')); } /** * It neither can do this. */ public function testVerifiesTheOrderOfCallsOnDifferentMethods() { $service = m::mock('PlanetIdentificator'); $service->shouldReceive('getPlanet')->withAnyArgs()->ordered(); $service->shouldReceive('getMoon')->withAnyArgs()->ordered(); $service->getPlanet('blue'); $service->getMoon('Earth', 'yellow'); } public function teardown() { m::close(); } } interface ColorService { public function getColor($planet); } class PlanetColorService implements ColorService { public function getColor($planet) {} } interface PlanetIdentificator { public function getPlanet($color); public function getMoon($planet, $color); }
Я использовал следующую загрузку:
<?php require_once 'Mockery/Loader.php'; require_once 'Hamcrest/hamcrest.php'; $loader = new \Mockery\Loader; $loader->register();
Выводы
С пуристской шляпой я бы сказал, что в случае, если вы должны создать свои тестовые двойники с каркасом, потому что они слишком сложны, чтобы издеваться вручную, вы уже в опасности.
Однако Mockery позволяет вам создавать макеты быстрее, и это будет очень хорошо для тестирования старого кода. Это также требует минимальной интеграции с PHPUnit: просто вызов метода в teardown () и автозагрузчик в начальной загрузке.
Таким образом, Mockery добавляет что-то полезное и для TDDing, поскольку сокращает продолжительность цикла Red-Green-Refactor; но имейте в виду, что вы не должны прибегать к определенным взломам нового кода.