[英]How can I unit test a method that dispatches events that alter behavior of the method under test?
我有一个具有公共属性的自定义事件:
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
属性设置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,最后两个选项具有模拟对象返回非理想对象的测试气味。 但是,取决于周围的体系结构,这可能是您必须走的方向。
创建要在方法中使用的对象总是有代码味道,并且使测试非常困难。 大多数事件处理方法都将事件作为参数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.