23

我正在学习单元测试,并试图解决以下问题:

Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for zfcUserAuthentication

...使用在以下给出的唯一答案:

使用 ZfcUser 的控制器的简单 ZF2 单元测试

所以我的 setUp 函数看起来是一样的。不幸的是,我收到错误消息:

Zend\Mvc\Exception\InvalidPluginException: Plugin of type Mock_ZfcUserAuthentication_868bf824 is invalid; must implement Zend\Mvc\Controller\Plugin\PluginInterface

这是在这部分代码引起的(在我的代码中以相同的方式拆分):

$this -> controller->getPluginManager()
->setService('zfcUserAuthentication', $authMock); // Error refers to this line.

$authMock 对象显然没有实现插件接口,我需要实现它才能传递给 setService。

$authMock 不打算在单元测试中使用吗?我应该使用不同的(面向单元测试的)setService 方法吗?

我需要一种方法来处理登录到我的应用程序,否则我的单元测试毫无意义。

感谢您的任何建议。

=== 编辑 (11/02/2013) ===

我想把重点放在这部分进行澄清,因为我认为这是问题区域:

// Getting mock of authentication object, which is used as a plugin.
$authMock = $this->getMock('ZfcUser\Controller\Plugin\ZfcUserAuthentication');

// Some expectations of the authentication service.
$authMock   -> expects($this->any())
    -> method('hasIdentity')
    -> will($this->returnValue(true));  

$authMock   -> expects($this->any())
    -> method('getIdentity')
    -> will($this->returnValue($ZfcUserMock));

// At this point, PluginManager disallows mock being assigned as plugin because 
// it will not implement plugin interface, as mentioned.
$this -> controller->getPluginManager()
->setService('zfcUserAuthentication', $authMock);

如果模拟没有处理必要的实现,我还能假装登录吗?

4

2 回答 2

3

您的名称间距或自动加载器有问题。

创建模拟时,ZfcUser\Controller\Plugin\ZfcUserAuthentication找不到的类定义。所以 PHPUnit 创建了一个模拟,它只为你的测试扩展这个类。如果该类可用,那么 PHPUnit 将在制作其模拟时使用实际类来扩展,然后将使用父类/接口。

你可以在这里看到这个逻辑:https ://github.com/sebastianbergmann/phpunit-mock-objects/blob/master/PHPUnit/Framework/MockObject/Generator.php

    if (!class_exists($mockClassName['fullClassName'], $callAutoload) &&
        !interface_exists($mockClassName['fullClassName'], $callAutoload)) {
        $prologue = 'class ' . $mockClassName['originalClassName'] . "\n{\n}\n\n";

        if (!empty($mockClassName['namespaceName'])) {
            $prologue = 'namespace ' . $mockClassName['namespaceName'] .
                        " {\n\n" . $prologue . "}\n\n" .
                        "namespace {\n\n";

            $epilogue = "\n\n}";
        }

        $cloneTemplate = new Text_Template(
          $templateDir . 'mocked_clone.tpl'
        );

所以如果没有类或接口,PHPUnit实际上会自己创建一个,这样mock就会满足原始类名的类型提示。但是,不会包含任何父类或接口,因为 PHPUnit 不知道它们。

这可能是由于您的测试中没有包含正确的命名空间或您的自动加载器出现问题。如果不实际查看整个测试文件,很难判断。


ZfcUser\Controller\Plugin\ZfcUserAuthentication或者,您可以在测试中模拟Zend\Mvc\Controller\Plugin\PluginInterface并将其传递给插件管理器,而不是 mocking 。尽管如果您在代码中对插件进行类型提示,您的测试仍然无法正常工作。

//Mock the plugin interface for checking authorization
$authMock = $this->getMock('Zend\Mvc\Controller\Plugin\PluginInterface');

// Some expectations of the authentication service.
$authMock   -> expects($this->any())
    -> method('hasIdentity')
    -> will($this->returnValue(true));  

$authMock   -> expects($this->any())
    -> method('getIdentity')
    -> will($this->returnValue($ZfcUserMock));

$this -> controller->getPluginManager()
->setService('zfcUserAuthentication', $authMock);
于 2014-01-15T14:54:26.710 回答
0

我刚刚为 FlashMessenger 插件做了一个示例。您应该只使用 ControllerPluginManager 来覆盖 ControllerPlugin。确保您的应用程序引导调用setApplicationConfig()

<?php
namespace SimpleTest\Controller;

use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;

class SimpleControllerTest extends AbstractHttpControllerTestCase {

  public function testControllerWillAddErrorMessageToFlashMessenger()
  {
      $flashMessengerMock = $this->getMockBuilder('\Zend\Mvc\Controller\Plugin\FlashMessenger', array('addErrorMessage'))->getMock();
      $flashMessengerMock->expects($this->once())
          ->method('addErrorMessage')
          ->will($this->returnValue(array()));


      $serviceManager = $this->getApplicationServiceLocator();
      $serviceManager->setAllowOverride(true);
      $serviceManager->get('ControllerPluginManager')->setService('flashMessenger', $flashMessengerMock);

      $this->dispatch('/error/message');

  }
}?>
于 2014-10-07T15:53:18.217 回答