我如何对调度事件的方法进行单元测试,这些事件会改变被测方法的行为?

时间:2015-12-04 20:11:51

标签: php unit-testing phpunit drupal-8

我有一个具有公共属性的自定义事件:

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 个答案:

答案 0 :(得分:1)

您无法控制事件对象的属性,因为您正在函数中创建对象。

有几种方法可以解决这个问题。

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 :(得分:1)

答案:只需创建一个可用于测试的可配置侦听器,并使其在每种情况下都按照您想要的方式运行。

不要这样做!这里的设计气味是事件接收器不应该改变发出事件的方法的逻辑。如果有2个听众怎么办?其中一个可以设置一个值而另一个可以设置另一个值?最后一个将赢,第一个听众不知道。

事件是一种通知另一个对象的方式,而不会让发射实体知道谁会听(也许没有其他对象)。关于有多少听众,发射器应该以相同的方式工作。如果您需要让某些其他对象控制逻辑的某些方面,请明确地执行(如果有疑问,请写另一个问题,我们会尽力帮助)