【问题标题】:ZF3 Unit test authentication onBootstrapZF3 单元测试认证 onBootstrap
【发布时间】:2016-12-28 18:03:28
【问题描述】:

我在为我的IndexController 班级运行单元测试时遇到问题。

单元测试只做以下事情(灵感来自unit-test tutorial of zf3):

IndexControllerTest.php:

public function testIndexActionCanBeAccessed()
{
    $this->dispatch('/', 'GET');
    $this->assertResponseStatusCode(200);
    $this->assertModuleName('main');
    $this->assertControllerName(IndexController::class); // as specified in router's controller name alias
    $this->assertControllerClass('IndexController');
    $this->assertMatchedRouteName('main');
}

Module.php 中,我有一些功能可以检查是否有用户登录,否则他将被重定向到login 路由。

Module.php:

public function onBootstrap(MvcEvent $mvcEvent)
{
    /** @var AuthService $authService */
    $authService = $mvcEvent->getApplication()->getServiceManager()->get(AuthService::class);
    $this->auth = $authService->getAuth(); // returns the Zend AuthenticationService object

    // store user and role in global viewmodel
    if ($this->auth->hasIdentity()) {
        $curUser = $this->auth->getIdentity();
        $mvcEvent->getViewModel()->setVariable('curUser', $curUser['system_name']);
        $mvcEvent->getViewModel()->setVariable('role', $curUser['role']);
        $mvcEvent->getApplication()->getEventManager()->attach(MvcEvent::EVENT_ROUTE, [$this, 'checkPermission']);
    } else {
        $mvcEvent->getApplication()->getEventManager()->attach(MvcEvent::EVENT_DISPATCH, [$this, 'authRedirect'], 1000);
    }
}

checkPermission 方法只是检查用户角色和匹配的路由是否在 acl 存储中。 如果失败,我将重定向状态码 404。

问题:单元测试失败:“Failed asserting response code “200”,实际状态码是“302” 因此,单元测试从我的 onBootstrap 方法跳转到发生重定向的 Module.php 中的 else 情况。

我在 TestCase 中执行了以下setUp,但它不起作用:

public function setUp()
{
    // override default configuration values
    $configOverrides = [];

    $this->setApplicationConfig(ArrayUtils::merge(
        include __DIR__ . '/../../../../config/application.config.php',
        $configOverrides
    ));

    $user = new Employee();
    $user->id = 1;
    $user->system_name = 'admin';
    $user->role = 'Admin';

    $this->authService = $this->prophesize(AuthService::class);
    $auth = $this->prophesize(AuthenticationService::class);
    $auth->hasIdentity()->willReturn(true);
    $auth->getIdentity()->willReturn($user);

    $this->authService->getAuth()->willReturn($auth->reveal());

    $this->getApplicationServiceLocator()->setAllowOverride(true);
    $this->getApplicationServiceLocator()->setService(AuthService::class, $this->authService->reveal());
    $this->getApplicationServiceLocator()->setAllowOverride(false);

    parent::setUp();
}

非常感谢提示

代码可能与 Zend Framework 2 有点不同,但如果您在 zf2 中有一个简单的工作示例,也许我可以将其转换为 zf3 样式。

我不使用 ZfcUser - 只是 zend-acl / zend-authentication 的东西

【问题讨论】:

    标签: php authentication phpunit zend-framework3


    【解决方案1】:

    经过几天的头痛,我找到了一个可行的解决方案。

    首先,我将onBootstrap 中的所有代码移至监听器,因为 phpunit 模拟是在 zf 引导之后生成的,因此在我的单元测试中不存在。

    关键是,服务是在我的可调用侦听器方法中生成的,该方法在 zf 完成引导后调用。 然后 PHPUnit 可以使用提供的 mock 覆盖服务。

    AuthenticationListener

    class AuthenticationListener implements ListenerAggregateInterface
    {
    use ListenerAggregateTrait;
    
    /**
     * @var AuthenticationService
     */
    private $auth;
    
    /**
     * @var Acl
     */
    private $acl;
    
    /**
     * Attach one or more listeners
     *
     * Implementors may add an optional $priority argument; the EventManager
     * implementation will pass this to the aggregate.
     *
     * @param EventManagerInterface $events
     * @param int $priority
     *
     * @return void
     */
    public function attach(EventManagerInterface $events, $priority = 1)
    {
        $this->listeners[] = $events->attach(MvcEvent::EVENT_ROUTE, [$this, 'checkAuthentication']);
    }
    
    /**
     * @param MvcEvent $event
     */
    public function checkAuthentication($event)
    {
        $this->auth = $event->getApplication()->getServiceManager()->get(AuthenticationService::class);
        $aclService = $event->getApplication()->getServiceManager()->get(AclService::class);
        $this->acl = $aclService->init();
    
        $event->getViewModel()->setVariable('acl', $this->acl);
        if ($this->auth->hasIdentity()) {
            $this->checkPermission($event);
        } else {
            $this->authRedirect($event);
        }
    }
    
    // checkPermission & authRedirect method
    }
    

    现在我的onBootstrap 变得非常小,就像 ZF 想要的那样。 documentation reference

    Module.php

    public function onBootstrap(MvcEvent $event)
    {
        $authListener = new AuthenticationListener();
        $authListener->attach($event->getApplication()->getEventManager());
    }
    

    最后我在单元测试中的 mocking 是这样的:

    IndexControllerTest

    private function authMock()
    {
        $mockAuth = $this->getMockBuilder(AuthenticationService::class)->disableOriginalConstructor()->getMock();
        $mockAuth->expects($this->any())->method('hasIdentity')->willReturn(true);
        $mockAuth->expects($this->any())->method('getIdentity')->willReturn(['id' => 1, 'systemName' => 'admin', 'role' => 'Admin']);
    
        $this->getApplicationServiceLocator()->setAllowOverride(true);
        $this->getApplicationServiceLocator()->setService(AuthenticationService::class, $mockAuth);
        $this->getApplicationServiceLocator()->setAllowOverride(false);
    }
    

    【讨论】:

    • 我已经尝试过你的解决方案,但我收到错误Zend\ServiceManager\Exception\ServiceNotFoundException: Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Zend\Authentication\AuthenticationService
    猜你喜欢
    • 2013-01-25
    • 1970-01-01
    • 2015-10-01
    • 1970-01-01
    • 2017-05-23
    • 2019-12-18
    • 1970-01-01
    • 2011-01-11
    • 2010-11-19
    相关资源
    最近更新 更多