Инверсия зависимостей (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()
.