【问题标题】:ZF2 unit-testing authenticationZF2 单元测试认证
【发布时间】:2013-01-25 22:38:42
【问题描述】:

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

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

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

Simple ZF2 Unit Tests for a controller using ZfcUser

所以我的 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);

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

【问题讨论】:

  • 我是否纠正了对控制器进行单元测试的必要性,因为它是模型?我发现这是我保存所有身份验证代码的地方。
  • 我最近做了类似的事情,没有问题。你完整的测试用例类是什么样的?另外,您的测试引导程序是什么样的?最后是您尝试测试的操作。
  • 单元测试时是否使用特殊的应用程序配置?在这种情况下,zfcUser 模块可能没有加载到测试环境中。

标签: phpunit zend-framework2 zfcuser


【解决方案1】:

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

创建模拟时,找不到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 不知道它们。

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


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

//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);

【讨论】:

    【解决方案2】:

    我刚刚为 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-06-22
      • 2016-12-28
      • 2015-07-07
      • 1970-01-01
      • 2013-04-24
      • 1970-01-01
      • 2013-03-01
      • 2015-03-06
      • 2017-03-16
      相关资源
      最近更新 更多