Инверсия зависимостей (Dependency Injection) — весьма приятная вещь, во многом облегчающая жизнь раз��аботчику. Но она же и является причиной появления таких вот конструкторов:
Использование
Допустим, у нас есть класс с указанным выше конструктором. Для мокирования окружения в отдельном тестовом методе нужно написать что-то такое:
Если количество зависимостей в объекте достаточно высоко, а реализуемый им функционал довольно сложен, то unit-тест может содержать изрядное количество блоков с инициализацией mock-объектов, поведение которых затем специализируется в соответствии с проверяемыми требованиями. А если изменилось количество зависимостей в конструкторе, то приходится добавлять новые mock-объекты в каждый тестовый метод и переделывать каждый
Следуя принципу DRY (Don't Repeat Yourself), следует сосредосточить создание mock'ов в одном месте, а затем уже специализировать их поведение в зависимости от условий тестирования в соответствующе�� тестовом методе. Это можно сделать при помощи функции
а затем прописываем в функции
после чего специализируем нужное нам поведение mock'ов в соответствующей тестирующей функции:
Отдельное спасибо Fesor за наводку, что лучше использовать
public function __construct( \Psr\Log\LoggerInterface $logger, \Zend_Db_Adapter_Pdo_Abstract $dba, ISomeService $service, ... ) { $this->_logger = $logger; $this->_dba = $dba; $this->_service = $service; ... }
Использование
setUp() в unit-тестах может существенно облегчить жизнь, если нужно несколько раз создать один и тот же набор mock'ов для тестирования различных особенностей реализации разрабатываемого класса.Допустим, у нас есть класс с указанным выше конструктором. Для мокирования окружения в отдельном тестовом методе нужно написать что-то такое:
/* create mocks */ $mLogger = $this->getMockBuilder(\Psr\Log\LoggerInterface::class)->getMock(); $mDba = $this->getMockBuilder(\Zend_Db_Adapter_Pdo_Abstract::class)->getMockForAbstractClass(); $mService = $this->getMockBuilder(\Vendor\Module\ISomeService::class)->disableOriginalConstructor()->getMock(); ... /* setup mocks behaviour */ ... /* */ $obj = new Demo($mLogger, $mDba, $mService, ...); $res = $obj->method($arg1, ...); $this->assert...
Если количество зависимостей в объекте достаточно высоко, а реализуемый им функционал довольно сложен, то unit-тест может содержать изрядное количество блоков с инициализацией mock-объектов, поведение которых затем специализируется в соответствии с проверяемыми требованиями. А если изменилось количество зависимостей в конструкторе, то приходится добавлять новые mock-объекты в каждый тестовый метод и переделывать каждый
$obj = new Demo(...);.Следуя принципу DRY (Don't Repeat Yourself), следует сосредосточить создание mock'ов в одном месте, а затем уже специализировать их поведение в зависимости от условий тестирования в соответствующе�� тестовом методе. Это можно сделать при помощи функции
setUp. Сначала создаем в PHPUnit'е свойства для самого тестируемого объекта и mock'ов зависимостей:private $mLogger; private $mDba; private $mService; private $obj
а затем прописываем в функции
setUp, вызываемую перед каждым тестовым методом, ре-инициализацию mock'ов и объектов:private function setUp() { $this->mLogger = $this->getMockBuilder(\Psr\Log\LoggerInterface::class)->getMock(); $this->mDba = $this->getMockBuilder(\Zend_Db_Adapter_Pdo_Abstract::class)->getMockForAbstractClass(); $this->mService = $this->getMockBuilder(\Vendor\Module\ISomeService::class)->disableOriginalConstructor()->getMock(); ... $this->obj = new Demo($this->mLogger, $this->mDba, $this->mService, ...); }
после чего специализируем нужное нам поведение mock'ов в соответствующей тестирующей функции:
public function test_method() { /* setup mocks behaviour */ $this->mLogger->expects... ... $res = $this->obj->method(); }
Отдельное спасибо Fesor за наводку, что лучше использовать
setUp(), а не костыль с extract().