【问题标题】:Cakephp 3 - behavior not loaded during controller unit testCakephp 3 - 控制器单元测试期间未加载的行为
【发布时间】:2016-11-24 12:44:38
【问题描述】:

我有一个通过初始化事件监听器添加到所有模型的行为。

// src/Event/InitializeEventListener.php 

...

class InitializeEventListener implements EventListenerInterface
{

    public function implementedEvents()
    {
        return [
            'Model.initialize' => 'initializeEvent'
        ];
    }

    public function initializeEvent(Event $event, $data = [], $options = [])
    {
        $event->subject()->addBehavior('MyBehavior');
    }

}


// bootstrap.php

$ieListener = new InitializeEventListener();
EventManager::instance()->on($ieListener);

我有UsersControllerindex 操作。如果我输入debug($this->Users->behaviors()->loaded());die;,我可以看到默认的Timestamp 和加载的MyBehavior。到目前为止一切正常,我可以在索引操作中使用MyBehavior 的函数。这是在浏览器中打开页面时。

现在,我有了这个测试功能

// tests/TestCase/Controller/UsersControllerTest.php
public function testIndex()
{
    $this->get('/users/index');
    $this->assertResponseSuccess();
}

但是,在运行测试时,MyBehavior 没有被加载(它不在加载的行为列表中,因此使用它的函数会产生未知方法错误)。我尝试将其添加到 UsersController 的测试用例中

public function setUp()
{
    parent::setUp();
    $this->Users = TableRegistry::get('Users');
    $eventList = new EventList();
    $eventList->add(new Event('Users.initializeEvent'));
    $this->Users->eventManager()->setEventList($eventList);
}

但是MyBehavior 还是没有加载。

谢谢

【问题讨论】:

    标签: cakephp phpunit cakephp-3.x


    【解决方案1】:

    每个测试都有一个新的全局事件管理器

    问题在于 CakePHP 测试用例在设置时设置了一个新的全局事件管理器实例:

    public function setUp()
    {
        // ...
        EventManager::instance(new EventManager());
    }
    

    https://github.com/cakephp/cakephp/blob/3.2.12/src/TestSuite/TestCase.php#L106

    因此,在此之前添加到全局事件管理器的所有内容都将丢失。

    这样做是为了避免状态。如果不这样做,那么您在测试方法 A 中的测试代码添加到全局管理器的可能侦听器仍然会出现在测试方法 B 中,这当然可能会导致那里测试的代码出现问题,例如侦听器堆叠,导致它们被多次调用,被调用的侦听器通常不会被调用,等等。

    之后添加全局侦听器作为解决方法

    每当我需要全局事件时,我都会将它们存储在配置值中,并在设置时将它们应用到测试用例中,类似于

    助推器

    $globalListeners = [
        new SomeListener(),
        new AnotherListener(),
    ];
    Configure::write('App.globalListeners', $globalListeners);
    foreach ($globalListeners as $listener) {
        EventManager::instance()->on($listener);
    }
    

    测试用例基类

    public function setUp()
    {
        parent::setUp();
    
        foreach (Configure::read('App.globalListeners') as $listener) {
            EventManager::instance()->on($listener);
        }
    }
    

    你的 sn-p 没有添加任何监听器

    话虽如此,您的设置代码 sn-p 的问题在于

    1. 它与监听事件无关,但与跟踪分派的事件有关。此外,您的应用中可能没有 Users.initializeEvent 事件,至少这是您的 InitializeEventListener 代码所暗示的。

    2. 实例化一个表类会导致Model.initialize事件被触发,所以即使你之后正确地添加了你的监听器,它也永远不会被触发,除非你已经清除了表注册表,这样users 表将在下一次 get() 调用时新建。

    【讨论】:

    • 想知道为什么会这样 - 刚刚发现了这个问题:github.com/cakephp/cakephp/pull/5268
    • @dav 是为了避免状态。如果不这样做,那么您在测试方法 A 中的测试代码添加到全局管理器的可能侦听器仍然会出现在测试方法 B 中,这当然可能会导致那里测试的代码出现问题,例如侦听器堆叠,导致它们被多次调用,被调用的侦听器通常不会被调用,等等......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-27
    • 1970-01-01
    • 2014-05-11
    • 2012-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多