У меня возникли проблемы с попыткой выполнить единичное тестирование действия, которое использует ZfcUser для аутентификации. Мне нужно каким-то образом высмеять плагин ZfcUser Controller, но я не уверен, как это сделать. Мне удалось успешно произвести некоторые модульные тесты для таблиц и моделей, но контроллер требует много инъецируемых объектов и вызывает проблемы. Кто-нибудь знает, как настроить Mocks 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()) {
которая вызывает это исключение, является контроллер: if (!$this->zfcUserAuthentication()->hasIdentity()) {
Эта строка относится к строке 974 в SmsController.
Очевидно, что у меня нет доступа к службе ZfcUserAuthentication, поэтому вопрос: как мне высмеять службу ZfcUserAuthentication и ввести ее в мой контроллер?
Чтобы продолжить тему, как я буду смеяться, чтобы зарегистрированный пользователь мог успешно протестировать мое действие, работает для спецификации?
Документация 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);
Может быть, более простой способ приветствовал бы другие мысли.
Вот как я это сделал.
<?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; } }