Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 我如何对一个方法进行单元测试,该方法发送事件来改变被测试方法的行为?_Php_Unit Testing_Phpunit_Drupal 8 - Fatal编程技术网

Php 我如何对一个方法进行单元测试,该方法发送事件来改变被测试方法的行为?

Php 我如何对一个方法进行单元测试,该方法发送事件来改变被测试方法的行为?,php,unit-testing,phpunit,drupal-8,Php,Unit Testing,Phpunit,Drupal 8,我有一个具有公共属性的自定义事件: class MyCustomEvent { public $allowAction = false; } 我有一个类创建这个事件,并用事件对象分派一个事件,允许事件侦听器/订阅者更改对象的属性 class MyBizLogic { private $dispatcher; public function __construct(EventDispatcher $dispatcher) { $this->

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

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
中创建事件,而是让dispatcher返回带有所需属性的
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.
       }));
在我看来,最后两个选项有模拟对象返回模拟对象的测试味道,这并不理想。但取决于周围的建筑,这可能是你必须走的方向


创建用于方法的对象总是一种代码味道,这使得测试非常困难。大多数事件处理方法都将事件作为参数。

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


不要那样做这里的设计意味着事件接收器不能改变发出事件的方法的逻辑。如果有两个听众呢?其中一个可以设置一个值,另一个可以设置另一个值?最后一个会赢,而第一个听众并不知道这一点


事件是一种通知另一个对象的方式,而发射实体不知道谁将侦听(可能没有其他对象)。发射器的工作方式应该与侦听器的数量相同。如果您需要让其他对象控制逻辑的某些方面,请明确地这样做(如果有疑问,请写另一个问题,我们将尽力提供帮助)

虽然我同意不在方法中直接实例化的便利性,但我认为在这种特殊情况下,问题与此无关。请确保属性应该由外部对象更改。因此,活动总是以同样的方式进行。我不同意这一点。我见过许多事件实现就是这样做的。请参见Symfony中的示例:。事件侦听器修改与已调度事件关联的表单对象。让事件监听器修改事件信息是很常见的。在symfony示例中,监听器对表单所做的更改不是表单工作所必需的。发射器不知道是否有任何其他对象正在侦听和动态添加其他场。如果任何其他侦听器需要进行相同的更改,它将注意到该字段已经添加,并且不会执行任何操作。总之,我的错是把答案和一个可能是评论的建议混为一谈。