ZF2 - Прошивка, запрошенная в Module.php
Я пытаюсь проверить контроллер моего приложения ZF2. Предположим, что этот контроллер находится в моем модуле A
.
В методе onBootstrap
Module.php
модуля A
я использую диспетчер служб для извлечения службы другого модуля, скажем B
, который я не загружаю.
Как установить макет запрашиваемой услуги в диспетчере сервисов? Имейте в виду, что я не могу использовать $this->getApplicationServiceLocator()
для этого в своем тесте, так как это уже вызывает метод Module.onBootstrap
моего модуля A
.
Чтобы опубликовать некоторый код, это то, что я делаю в данный момент
bootstrap.php
namespace Application;
use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\ServiceManager\ServiceManager;
use RuntimeException;
class Bootstrap
{
protected static $serviceManager;
public static function init()
{
$modulePath = static::findParentPath('module');
$vendorPath = static::findParentPath('vendor');
if (is_readable($vendorPath . '/autoload.php')) {
$loader = include $vendorPath . '/autoload.php';
} else {
throw new RuntimeException('Cannot locate autoload.php');
}
$config = [
'modules' => [
'Application',
],
'module_listener_options' => [
'module_paths' => [
$modulePath,
$vendorPath
]
]
];
$serviceManager = new ServiceManager(new ServiceManagerConfig());
$serviceManager->setService('ApplicationConfig', $config);
$serviceManager->get('ModuleManager')->loadModules();
static::$serviceManager = $serviceManager;
}
protected static function findParentPath($path)
{
$dir = __DIR__;
$previousDir = '.';
while (!is_dir($dir . '/' . $path)) {
$dir = dirname($dir);
if ($previousDir === $dir) {
return false;
}
$previousDir = $dir;
}
return $dir . '/' . $path;
}
public static function getServiceManager()
{
return static::$serviceManager;
}
}
Bootstrap::init();
мой фактический класс тестирования
namespace Application\Functional;
use Application\Bootstrap;
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
class ValidateCustomerRegistrationTest extends AbstractHttpControllerTestCase
{
public function setUp()
{
$serviceManager = Bootstrap::getServiceManager();
$applicationConfig = $serviceManager->get('ApplicationConfig');
$this->setApplicationConfig($applicationConfig);
parent::setUp();
}
public function testRegisterValidUserWithOnlyEquomobiliData()
{
$this->getApplicationServiceLocator();
}
}
Module.php упрощен
namespace Application
Class Module
{
public function onBootstrap(MvcEvent $e)
{
$serviceManager = $e->getApplication()->getServiceManager();
$service = $serviceManager->get('Service\From\Other\Module');
}
}
Ответы
Ответ 1
Здесь недостаточно данных, чтобы помочь вам напрямую. Было бы более полезно иметь функцию ValidateCustomerRegistrationTest->getApplicationServiceLocator()
для стартеров.
Надеюсь, я косвенно помогу вам.
Рефакторинг может быть полезным
Когда я пишу unit test, я начинаю с нескольких личных правил.
Проверяйте только тестируемый код. Откажитесь от ВСЕХ. Нет необходимости тестировать что-то, что уже должно иметь собственные тесты.
Как работает функция, не должно быть важно. Только вход/выход. Это гарантирует, что ваш тест будет жизнеспособным даже тогда, когда корень функции резко изменится.
/**
* @param MvcEventInterface $event
* @param Service\From\Other\ModuleInterface $service
*
* @return boolean
*/
public function onBootstrap(MvcEventInterface $event, Service\From\Other\ModuleInterface $service)
{
return true;
}
Тогда в тестовом классе:
public function testOnBootstrap(){
$eventMock = $this->getMock(MvcEventInterface::class);
$serviceMock = $this->getMock(ModuleInterface::class);
$module = new Module();
$result = $module->onBootstrap($eventMock, $serviceMock);
$this->assertTrue($result);
}
* Я точно не знаю, что вы пытаетесь проверить
Если рефакторинг не является опцией
Есть два типа макетов, которые могут помочь, которые возникают у меня сразу с места в карьер, насмехаются и mockBuilder. Взгляните на документацию PHPUnit для двухэтажного тестирования.
$serviceMock = $this->getMockBuilder(ServiceManager::class)
->disableOriginalConstructor()
->setMethods([
'__construct',
'get'
])
->getMock();
$serviceMock
->expects($this->any())
->method('get')
->will($this->returnValue(
$this->getMock(ModuleManagerInterface::class)
));
Удачи, надеюсь, вы можете сообщить нам, если мы сможем помочь вам более конкретно. Я бы также рекомендовал изучить SOLID Принципы объектно-ориентированного программирования. Один из многих принципов программирования, который должен сделать ваш код чистым, легким для расширения и простым в тестировании.
Ответ 2
Один из способов добиться того, что вы пытаетесь сделать, - это принудительно перезаписать службу в Service Manager, прежде чем отправлять запрос на ваш тестовый контроллер. Вот пример о том, как это сделать (ПРИМЕЧАНИЕ: поскольку то, что вы делаете во время процесса начальной загрузки модуля, пример может не переводить 100 % к вашей ситуации).
Как и другие люди, вы можете еще раз проверить, можете ли вы реорганизовать более чистый подход, но это другая история.
Ответ 3
Чтобы высмеять менеджера сервисов и сделанные с ним вызовы, вы можете использовать mockery https://github.com/mockery/mockery. Эта библиотека является агностикой рамки, поэтому, даже если вы используете PHPUnit или другой инструмент, он должен работать.
Плохой способ использовать его, но он должен быстро решить вашу проблему - перегрузить класс, используя mockingery ('overload: myclass'), где myclass является экземпляром диспетчера служб.
Лучший способ использовать библиотеку - использовать шаблон внедрения depedency, чтобы ввести макет локатора сервисов, когда ваше приложение повышает эффективность тестов.
Легкий способ сделать это - создать factory для вашего контроллера, ввести локатор службы внутри. Затем в вашем тесте вы запускаете контроллер с макетом локатора службы в качестве параметра.