Cakephp 3 - 控制器单元测试期间未加载行为

Cakephp 3 - behavior not loaded during controller unit test

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

// 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 测试用例在设置时设置了一个新的全局事件管理器实例:

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

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

所以在该点之前添加到全局事件管理器的所有内容都将丢失。

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

在较新的 CakePHP 版本中使用 Application::bootstrap()

从 CakePHP 3.3 开始,您可以在应用程序的 Application class 中使用 bootstrap() 方法,与仅包含一次的 config/bootstrap.php 文件不同,正在为每个集成测试请求调用方法。

通过这种方式,您的全局事件将在测试用例分配一个新的干净事件管理器实例后添加。

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

在 CakePHP 3.3 之前的版本中,每当我需要 gloabl 事件时,我都会将它们存储在配置值中并在测试用例的设置时应用它们,类似于

助推器

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

测试用例库class

public function setUp()
{
    parent::setUp();

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

您的代码段未添加任何侦听器

也就是说,您的设置代码片段的问题在于

  1. 与监听事件无关,与跟踪派发的事件有关。此外,您的应用中可能没有 Users.initializeEvent 事件,至少您的 InitializeEventListener 代码是这样建议的。

  2. 实例化 table class 将导致 Model.initialize 事件被触发,因此即使您之后正确添加了监听器,它也永远不会除非您清除 table 注册表,否则会被触发,这样用户 table 将在下一个 get() 调用时新建。