PHPUnit: простой синтаксис для создания mock-объектов

от автора

  1. почему не mockery
  2. основное преимущество — синтаксис
  3. моки — нативные
  4. создание stub-объектов
  5. мокирование свойств класса
  6. mock injection
  7. удобные методы для работы с Reflection
  8. как добавить в проект

Почему не Mockery

PHPUnit — стандартный, наиболее распространенный фреймворк для написания unit-тестов в мире PHP. Ничего удивительного — он хорошо справляется с возложенными на него обязанностями. Но если говорить о стандартном синтаксисе создания mock-объектов, многие люди жалуются на его некоторую громоздкость. Они предлагают различные плагины для создания моков, такие как Mockery (я понимаю, что это не просто плагин).

Однако, я уверен что PHPUnit обладает достаточно хорошо развитой системой для создания моков, чтобы продолжать его использовать. PHPUnit активно развивается: не так давно были добавлены мокирование трейтов, мокирование несуществующих классов — для TDD.

И этот проект XPMock — это способ упростить синтаксис создания моков. Нужно подчеркнуть, что XPMock не создает собственные моки, и не делает никаких оберток над моками PHPUnit. XPMock вызывает те же самые методы PHPUnit, создавая тем самым те же самые моки, только несколько проще.

Основное преимущество — синтаксис

Стандартный синтаксис для создания мока (объекта-заглушки с тремя методами) в PHPUnit выглядит так:

$mock = $this->getMockBuilder('MyClass')    ->setMethods(['getBool', 'getNumber', 'getString'])    ->disableOriginalConstructor()    ->getMock(); $mock->expects($this->any())    ->method('getBool')    ->will($this->returnValue(true)); $mock->expects($this->any())    ->method('getNumber')    ->will($this->returnValue(1)); $mock->expects($this->any())    ->method('getString')    ->will($this->returnValue('string')); 

Те, кто мокируют большую часть зависимостей в unit-тестах, замечают, что код теста очень быстро разрастается именно из-за таких конструкций.

Если же использовать XPMock, то создание мока становится значительно короче:

$this->mock('MyClass')    ->getBool(true)    ->getNumber(1)    ->getString('string')    ->new(); 

Моки — нативные

XPMock
— не создает свои собственные моки
— не делает дополнительных оберток над моками PHPUnit
— поддерживает все нативные конструкции PHPUnit

Например,

$mock->getNumber($this->once()) 

это то же самое, что написать

$mock->expects($this->once())     ->method('getNumber')     ->will($this->returnValue(null)) 

Другие примеры короткой записи часто используемых конструкций можно посмотреть здесь: github.com/ptrofimov/xpmock

Создание stub-объектов

Stub-объекты, или объекты-заглушки, — это мок-объекты, у которых все методы по умолчанию перекрывают реальные методы и возвращают null. В PHPUnit в синтаксисе нет деления на моки, стабы и иже с ними. Все искусственные объекты для тестирования создаются с помощью getMock или getMockBuilder.

В XPMock включен специальный метод stub который вернет мок, у которого все методы по умолчанию возвращают null. Это и улучшает читаемость кода, и избавляет от необходимости вызывать метод setMethods при создании реальных моков.

Мокирование свойств класса

PHPUnit не умеет мокировать свойства класса. XPMock — тоже. Однако он предоставляет удобные методы задания свойств у создаваемого мока через Reflection. Указанные при создании мока присваивания будут выполнены сразу после вызова метода-конструктора new.

$this->mock(‘MyClass’)      ->__set(‘property’, $value)      ->new(); 

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

Mock injection

Очень часто при создании моков их нужно сразу же внедрить в другие объекты. Например, создать instance синглтона и внедрить ссылку на него в статическую переменную instance того же класса. Обычно это делается через Reflection. XPMock и здесь предоставляет удобный синтаксис для таких действий.

$this->mock(‘MyClass’)     ->injectTo($object, ‘property’)     ->new(); 

Удобные методы для работы с Reflection

XPMock предоставляет и общий короткий синтаксис для работы с объектами через Reflection.

Например, чтобы получить закрытое свойство объекта, обычно нужно писать так:

$property = new \ReflectionProperty(‘MyClass’, ‘property’); $property->setAccessible(true); $value = $property->getValue($object); 

С XPMock можно это делать так:

$value = $this->reflect('MyClass')->property; 

Подобным синтаксисом можно получать значения закрытых/открытых статических/нестатических свойств объектов, а также вызывать закрытые методы.

Как добавить в проект

XPMock легко встраивается в существующие тесты и не мешает работать нативному синтаксису создания моков PHPUnit.

Вариант 1. Добавить трейт XPMock к существующему тесту (подходит для PHP>=5.4)

class MyTestCase extends \PHPUnit_Framework_TestCase {     use \Xpmock\TestCaseTrait; } 

Вариант 2. Унаследовать класс теста от соответстующего класса XPMock (подходит для PHP>=5.3)

class MyTestCase extends \Xpmock\TestCase { } 

Добавить XPMock в проект очень просто — через менеджер зависимостей composer. Инструкция по установке из 2-х шагов здесь — github.com/ptrofimov/xpmock#installation

ссылка на оригинал статьи http://habrahabr.ru/post/183010/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *