【问题标题】:Simple ZF2 Unit Tests for a controller using ZfcUser使用 ZfcUser 的控制器的简单 ZF2 单元测试
【发布时间】: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-&gt;zfcUserAuthentication()-&gt;hasIdentity()) { 该行与 SmsController 中的第 974 行相关。

很明显我无权访问 ZfcUserAuthentication 服务,所以问题是,如何模拟 ZfcUserAuthentication 服务并将其注入到我的 Controller 中?

要继续主题,我将如何模拟登录用户以成功测试我的操作是否符合规范?

【问题讨论】:

    标签: php unit-testing phpunit zend-framework2 zfcuser


    【解决方案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;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-08-28
        • 1970-01-01
        • 2014-09-13
        • 2018-08-03
        • 2014-06-27
        • 2014-04-12
        • 2019-04-18
        相关资源
        最近更新 更多