使用 ZfcUser 的控制器的简单 ZF2 单元测试
Posted
技术标签:
【中文标题】使用 ZfcUser 的控制器的简单 ZF2 单元测试【英文标题】:Simple ZF2 Unit Tests for a controller using ZfcUser 【发布时间】:2012-12-30 12:55:33 【问题描述】:我在尝试对使用 ZfcUser 进行身份验证的操作进行单元测试时遇到问题。我需要一些方法来模拟 ZfcUser 控制器插件,但我不太确定如何做到这一点。我已经成功地为表和模型生成了一些单元测试,但是控制器需要大量注入的对象并导致问题。有谁知道如何设置 ZfcUser 模拟以成功对控制器进行单元测试?
这是我的测试(复制自 ZF2 教程):
<?php
namespace SmsTest\Controller;
use SmsTest\Bootstrap;
use Sms\Controller\SmsController;
use Zend\Http\Request;
use Zend\Http\Response;
use Zend\Mvc\MvcEvent;
use Zend\Mvc\Router\RouteMatch;
use Zend\Mvc\Router\Http\TreeRouteStack as HttpRouter;
use PHPUnit_Framework_TestCase;
class SmsControllerTest extends PHPUnit_Framework_TestCase
protected $controller;
protected $request;
protected $response;
protected $routeMatch;
protected $event;
protected function setUp()
$serviceManager = Bootstrap::getServiceManager();
$this->controller = new SmsController();
$this->request = new Request();
$this->routeMatch = new RouteMatch(array('controller' => 'index'));
$this->event = new MvcEvent();
$config = $serviceManager->get('Config');
$routerConfig = isset($config['router']) ? $config['router'] : array();
$router = HttpRouter::factory($routerConfig);
$this->event->setRouter($router);
$this->event->setRouteMatch($this->routeMatch);
$this->controller->setEvent($this->event);
$this->controller->setServiceLocator($serviceManager);
/* Test all actions can be accessed */
public function testIndexActionCanBeAccessed()
$this->routeMatch->setParam('action', 'index');
$result = $this->controller->dispatch($this->request);
$response = $this->controller->getResponse();
$this->assertEquals(200, $response->getStatusCode());
我在setUp方法中尝试了以下:
$mockAuth = $this->getMock('ZfcUser\Entity\UserInterface');
$authMock = $this->getMock('Zend\Authentication\AuthenticationService');
$authMock->expects($this->any())
->method('hasIdentity')
->will($this->returnValue(true));
$authMock->expects($this->any())
->method('getIdentity')
->will($this->returnValue(array('user_id' => 1)));
但我不确定如何将它注入到控制器实例中。
假设我的索引操作代码如下:
public function indexAction()
//Check if logged in
if (!$this->zfcUserAuthentication()->hasIdentity())
return $this->redirect()->toRoute('zfcuser/login');
return new ViewModel(array(
'success' => true,
));
测试结果:
1) SmsTest\Controller\SmsControllerTest::testIndexActionCanBeAccessed
Zend\ServiceManager\Exception\ServiceNotFoundException: Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for zfcUserAuthentication
/var/www/soap-app.localhost/Zend/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:450
/var/www/soap-app.localhost/Zend/vendor/zendframework/zendframework/library/Zend/ServiceManager/AbstractPluginManager.php:110
/var/www/soap-app.localhost/Zend/vendor/zendframework/zendframework/library/Zend/Mvc/Controller/PluginManager.php:90
/var/www/soap-app.localhost/Zend/vendor/zendframework/zendframework/library/Zend/Mvc/Controller/AbstractController.php:276
/var/www/soap-app.localhost/Zend/vendor/zendframework/zendframework/library/Zend/Mvc/Controller/AbstractController.php:291
/var/www/soap-app.localhost/Zend/module/Sms/src/Sms/Controller/SmsController.php:974
/var/www/soap-app.localhost/Zend/module/Sms/src/Sms/Controller/SmsController.php:974
/var/www/soap-app.localhost/Zend/module/Sms/src/Sms/Controller/SmsController.php:158
/var/www/soap-app.localhost/Zend/vendor/zendframework/zendframework/library/Zend/Mvc/Controller/AbstractActionController.php:87
/var/www/soap-app.localhost/Zend/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php:468
/var/www/soap-app.localhost/Zend/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php:208
/var/www/soap-app.localhost/Zend/vendor/zendframework/zendframework/library/Zend/Mvc/Controller/AbstractController.php:108
/var/www/soap-app.localhost/Zend/module/Sms/test/SmsTest/Controller/SmsControllerTest.php:57
导致此异常的行是控制器:如果(!$this->zfcUserAuthentication()->hasIdentity())
该行与 SmsController 中的第 974 行相关。
很明显我无权访问 ZfcUserAuthentication 服务,所以问题是,如何模拟 ZfcUserAuthentication 服务并将其注入到我的 Controller 中?
要继续主题,我将如何模拟登录用户以成功测试我的操作是否符合规范?
【问题讨论】:
【参考方案1】:ZfcUser 文档表明这是一个插件,因此您需要将其注入控制器。
你需要修改你的类名来选择 ZfcUser 类
您的模拟也需要添加,因为 getIdenty 返回不同的对象。
以下内容对我有用 - 插入您的 phpunit setUp() 方法。
$serviceManager = Bootstrap::getServiceManager();
$this->controller = new RegisterController();
$this->request = new Request();
$this->routeMatch = new RouteMatch(array('controller' => 'add'));
$this->event = new MvcEvent();
$config = $serviceManager->get('Config');
$routerConfig = isset($config['router']) ? $config['router'] : array();
$router = HttpRouter::factory($routerConfig);
$this->event->setRouter($router);
$this->event->setRouteMatch($this->routeMatch);
$this->controller->setEvent($this->event);
$this->controller->setServiceLocator($serviceManager);
$mockAuth = $this->getMock('ZfcUser\Entity\UserInterface');
$ZfcUserMock = $this->getMock('ZfcUser\Entity\User');
$ZfcUserMock->expects($this->any())
->method('getId')
->will($this->returnValue('1'));
$authMock = $this->getMock('ZfcUser\Controller\Plugin\ZfcUserAuthentication');
$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】:我就是这样做的。
<?php
namespace IssueTest\Controller;
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
class IssueControllerTest extends AbstractHttpControllerTestCase
protected $serviceManager;
public function setUp()
$this->setApplicationConfig(
include '/media/policybubble/config/application.config.php'
);
parent::setUp();
$ZfcUserMock = $this->getMock('ZfcUser\Entity\User');
$ZfcUserMock->expects($this->any())
->method('getId')
->will($this->returnValue('1'));
$authMock = $this->getMock(
'ZfcUser\Controller\Plugin\ZfcUserAuthentication'
);
$authMock->expects($this->any())
->method('hasIdentity')
->will($this->returnValue(true));
$authMock->expects($this->any())
->method('getIdentity')
->will($this->returnValue($ZfcUserMock));
$this->serviceManager = $this->getApplicationServiceLocator();
$this->serviceManager->setAllowOverride(true);
$this->serviceManager->get('ControllerPluginManager')->setService(
'zfcUserAuthentication', $authMock
);
public function testIndexActionCanBeAccessed()
$this->dispatch('/issue');
$this->assertResponseStatusCode(200);
$this->assertModuleName('Issue');
$this->assertControllerName('Issue\Controller\Issue');
$this->assertControllerClass('IssueController');
$this->assertMatchedRouteName('issue');
public function testAddActionRedirectsAfterValidPost()
$issueTableMock = $this->getMockBuilder('Issue\Model\IssueTable')
->disableOriginalConstructor()
->getMock();
$issueTableMock->expects($this->once())
->method('saveIssue')
->will($this->returnValue(null));
$this->serviceManager->setService('Issue\Model\IssueTable', $issueTableMock);
$postData = array(
'title' => 'Gun Control',
'id' => '',
);
$this->dispatch('/issue/add', 'POST', $postData);
$this->assertResponseStatusCode(302);
$this->assertRedirectTo('/issue');
public function testEditActionRedirectsAfterValidPost()
$issueTableMock = $this->getMockBuilder('Issue\Model\IssueTable')
->disableOriginalConstructor()
->getMock();
$issueTableMock->expects($this->once())
->method('saveIssue')
->will($this->returnValue(null));
$this->serviceManager->setService('Issue\Model\IssueTable', $issueTableMock);
$issueTableMock->expects($this->once())
->method('getIssue')
->will($this->returnValue(new \Issue\Model\Issue()));
$postData = array(
'title' => 'Gun Control',
'id' => '1',
);
$this->dispatch('/issue/edit/1', 'POST', $postData);
$this->assertResponseStatusCode(302);
$this->assertRedirectTo('/issue');
public function testDeleteActionRedirectsAfterValidPost()
$postData = array(
'title' => 'Gun Control',
'id' => '1',
);
$this->dispatch('/issue/delete/1', 'POST', $postData);
$this->assertResponseStatusCode(302);
$this->assertRedirectTo('/issue');
<?php
namespace Issue\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Issue\Model\Issue;
use Issue\Form\IssueForm;
class IssueController extends AbstractActionController
protected $issueTable;
public function indexAction()
if (!$this->zfcUserAuthentication()->hasIdentity())
return;
return new ViewModel(
array(
'issues' => $this->getIssueTable()->fetchAll(
$this->zfcUserAuthentication()->getIdentity()->getId()
),
)
);
public function addAction()
if (!$this->zfcUserAuthentication()->hasIdentity())
return $this->redirect()->toRoute('issue');
$form = new IssueForm();
$form->get('submit')->setValue('Add');
$request = $this->getRequest();
if ($request->isPost())
$issue = new Issue();
$form->setInputFilter($issue->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid())
$issue->exchangeArray($form->getData());
$this->getIssueTable()->saveIssue(
$issue,
$this->zfcUserAuthentication()->getIdentity()->getId()
);
// Redirect to list of issues
return $this->redirect()->toRoute('issue');
return array('form' => $form);
public function editAction()
if (!$this->zfcUserAuthentication()->hasIdentity())
return $this->redirect()->toRoute('issue');
$id = (int)$this->params()->fromRoute('id', 0);
if (!$id)
return $this->redirect()->toRoute(
'issue', array(
'action' => 'add'
)
);
// Get the Issue with the specified id. An exception is thrown
// if it cannot be found, in which case go to the index page.
try
$issue = $this->getIssueTable()->getIssue($id);
catch (\Exception $ex)
return $this->redirect()->toRoute(
'issue', array(
'action' => 'index'
)
);
$form = new IssueForm();
$form->bind($issue);
$form->get('submit')->setAttribute('value', 'Edit');
$request = $this->getRequest();
if ($request->isPost())
$form->setInputFilter($issue->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid())
$this->getIssueTable()->saveIssue(
$issue,
$this->zfcUserAuthentication()->getIdentity()->getId()
);
// Redirect to list of issues
return $this->redirect()->toRoute('issue');
return array(
'id' => $id,
'form' => $form,
);
public function deleteAction()
if (!$this->zfcUserAuthentication()->hasIdentity())
return $this->redirect()->toRoute('issue');
$id = (int)$this->params()->fromRoute('id', 0);
if (!$id)
return $this->redirect()->toRoute('issue');
$request = $this->getRequest();
if ($request->isPost())
$del = $request->getPost('del', 'No');
if ($del == 'Yes')
$id = (int)$request->getPost('id');
$this->getIssueTable()->deleteIssue($id);
// Redirect to list of issues
return $this->redirect()->toRoute('issue');
return array(
'id' => $id,
'issue' => $this->getIssueTable()->getIssue($id)
);
public function getIssueTable()
if (!$this->issueTable)
$sm = $this->getServiceLocator();
$this->issueTable = $sm->get('Issue\Model\IssueTable');
return $this->issueTable;
【讨论】:
以上是关于使用 ZfcUser 的控制器的简单 ZF2 单元测试的主要内容,如果未能解决你的问题,请参考以下文章