У меня есть пользовательское событие, которое имеет публичное свойство:
class MyCustomEvent
{
public $allowAction = false;
}
У меня есть класс, который создает это событие и отправляет событие с объектом события, позволяя слушателям событий / подписчикам изменять свойство объекта.
class MyBizLogic
{
private $dispatcher;
public function __construct(EventDispatcher $dispatcher)
{
$this->dispatcher = $dispatcher;
}
public function doSomething()
{
$event = new MyCustomEvent();
$dispatcher = $this->dispatcher->dispatch('my_custom_event', $event);
if ($event->allowAction) {
// do action
} else {
// do something else
}
}
}
Как я могу юнит тест doSomething
? Мне нужен способ управления свойствами объекта события, но объект события не является зависимостью, которую я могу издеваться. Он создан в рамках метода, который я тестирую.
Я не думаю, что это запах дизайна, потому что именно так воображают большинство разработчиков события. Что я могу сделать здесь, чтобы правильно проверить различные результаты, которые doSomething
должен иметь дело с?
Вы не можете управлять свойствами объекта события, потому что вы создаете объект в своей функции.
Есть несколько способов обойти это.
1) Сделайте так, чтобы ваш метод doSomething взял MyCustomEvent
объект в качестве аргумента. Тогда вы сможете передать фиктивный объект и таким образом управлять им.
2) Не создавайте событие в doSomething
вместо этого попросите диспетчера вернуть MyCustomEvent
со свойствами, которые вы хотите. Таким образом, в вашем тесте у вас будет mockDispatcher
что бы вернуть объект события из dispatch
метод.
3) Передайте объект фабрики событий, который вы можете использовать для получения экземпляров правильного события. Затем вы можете смоделировать это и заставить его возвращать объект фиктивного события для вас.
4) Вы можете использовать функцию обратного вызова для dispatch
метод для диспетчера событий. Ваша функция может затем установить MyCustomEvent::$allowAction
собственность на то, что вы хотите.
$allowAction = 'foo';
$mockEventDispatcher->expects($this->once())
->method('dispatch')
->with('my_custom_event', $this->isInstanceOf('MyCustomEvent')
->will($this->returnCallback(function($string, $event) use ($allowAction) {
$event->allowAction = $allowAction
// Return whatever the dispatcher is supposed to return.
}));
IMO, последние два варианта имеют тестовый запах ложных объектов, возвращающих ложные объекты, что не идеально. Но в зависимости от окружающей архитектуры, это может быть направление, в котором вы должны идти.
Создание объектов для использования в методе всегда является запахом кода и делает тестирование очень трудным. Большинство методов обработки событий принимают событие в качестве аргумента.
Ответ. Просто создайте настраиваемый прослушиватель, который вы будете использовать для тестов, и сделайте так, чтобы он вел себя так, как вы хотите в каждом случае.
не делай этого! Запах конструкции заключается в том, что получатели событий не должны иметь возможности изменять логику метода, который генерирует событие. Что если бы было 2 слушателя? Один из них может установить значение, а другой установить другое значение? Последний победит, а первый слушатель этого не знает.
События — это способ уведомления других объектов без того, чтобы излучающая сущность не знала, кто будет слушать (может быть, нет другого объекта). Эмитент должен работать одинаково в отношении количества слушателей. Если вам нужен какой-то другой объект, контролирующий некоторые аспекты логики, сделайте это явно (если сомневаетесь, напишите другой вопрос, и мы постараемся помочь)