Простые модульные тесты ZF2 для контроллера с использованием ZfcUser

У меня проблемы с попыткой модульного тестирования действия, которое использует ZfcUser для аутентификации. Мне нужен какой-то способ издеваться над плагином ZfcUser Controller, но я не совсем уверен, как это сделать. Мне удалось успешно произвести некоторые модульные тесты для таблиц и моделей, но контроллер требует много внедренных объектов и вызывает проблемы. Кто-нибудь знает, как настроить макеты 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()) {Эта строка относится к строке 974 в SmsController.

Очевидно, что у меня нет доступа к сервису ZfcUserAuthentication, поэтому вопрос в том, как мне посмеяться над сервисом ZfcUserAuthentication и внедрить его в мой контроллер?

Чтобы продолжить тему, как бы я начал издеваться над вошедшим в систему пользователем, чтобы успешно проверить, что мое действие работает в соответствии со спецификацией?

2 ответа

Решение

Документация 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;
    }
}
Другие вопросы по тегам