ZF3单元测试身份验证onBootstrap [英] ZF3 Unit test authentication onBootstrap
问题描述
在为IndexController
类运行单元测试时遇到问题.
I have a problem getting a unit test to run for my IndexController
class.
单元测试仅执行以下操作(灵感来自 zf3的单元测试教程):
The unit test just does the following (inspired from the 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
路由.
In the Module.php
I've some functionality to check if there is a user logged in, else he will be redirected to a login
route.
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.
The checkPermission
method just checks if the user role and the matched route are in the acl storage.
If this fails I will redirect a status code of 404.
问题:单元测试失败:声明响应代码"200"失败,实际状态代码为"302"
因此,单元测试从发生重定向的Module.php
中的onBootstrap
方法跳转到else情况.
Problem: The unit test fails: "Failed asserting response code "200", actual status code is "302"
Therefore the unit test jumps into the else case from my onBootstrap
method in the Module.php
where the redirect happen.
我在 TestCase 中执行了以下setUp
,但是它不起作用:
I did the following setUp
in the TestCase but it doesn't work:
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样式.
The code might differ a bit from Zend Framework 2 but If you have a simple working example in zf2 maybe I can transform it into zf3 style.
我不使用ZfcUser-只是zend-acl/zend-authentication之类的东西
I don't use ZfcUser - just the zend-acl / zend-authentication stuff
推荐答案
头痛几天后,我有了一个可行的解决方案.
After several days of headache I've got a working solution.
首先,我将onBootstrap
中的所有代码移到了侦听器,因为phpunit模拟是在zf引导程序之后生成的,因此在我的单元测试中不存在.
First I moved all the code within the onBootstrap
to a Listener, because the phpunit mocks are generated after the zf bootstrapping and therefore are non existent in my unit tests.
关键是,这些服务是在我的可调用侦听器方法中生成的,该方法在zf完成引导之后调用. 然后,PHPUnit可以使用提供的模拟覆盖服务.
The key is, that the services are generated in my callable listener method, which is called after zf finished bootstrapping. Then PHPUnit can override the service with the provided 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想要的那样. 文档参考
Now my onBootstrap
got really small, just like ZF wants it. documentation reference
Module.php
public function onBootstrap(MvcEvent $event)
{
$authListener = new AuthenticationListener();
$authListener->attach($event->getApplication()->getEventManager());
}
最后,我在单元测试中的模拟如下:
Finally my mocking in the unit test looks like this:
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);
}
这篇关于ZF3单元测试身份验证onBootstrap的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!