Как я могу выполнить модульное тестирование метода, который отправляет события, которые изменяют поведение тестируемого метода?

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

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 должен иметь дело с?

2

Решение

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

Есть несколько способов обойти это.

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, последние два варианта имеют тестовый запах ложных объектов, возвращающих ложные объекты, что не идеально. Но в зависимости от окружающей архитектуры, это может быть направление, в котором вы должны идти.

Создание объектов для использования в методе всегда является запахом кода и делает тестирование очень трудным. Большинство методов обработки событий принимают событие в качестве аргумента.

1

Другие решения

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


не делай этого! Запах конструкции заключается в том, что получатели событий не должны иметь возможности изменять логику метода, который генерирует событие. Что если бы было 2 слушателя? Один из них может установить значение, а другой установить другое значение? Последний победит, а первый слушатель этого не знает.

События — это способ уведомления других объектов без того, чтобы излучающая сущность не знала, кто будет слушать (может быть, нет другого объекта). Эмитент должен работать одинаково в отношении количества слушателей. Если вам нужен какой-то другой объект, контролирующий некоторые аспекты логики, сделайте это явно (если сомневаетесь, напишите другой вопрос, и мы постараемся помочь)

1

По вопросам рекламы [email protected]