2013-02-10 14 views
23

uczyłem się o testów jednostkowych i starałem się rozwiązać następujący problem:ZF2 uwierzytelniania jednostka testowanie

Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for zfcUserAuthentication 

... używając tylko odpowiedź udzieloną na:

Simple ZF2 Unit Tests for a controller using ZfcUser

So moja funkcja setUp wygląda tak samo. Niestety, pojawia się komunikat o błędzie:

Zend\Mvc\Exception\InvalidPluginException: Plugin of type Mock_ZfcUserAuthentication_868bf824 is invalid; must implement Zend\Mvc\Controller\Plugin\PluginInterface 

jest to spowodowane w tej części kodu (Split w moim kodzie w taki sam sposób): Object

$this -> controller->getPluginManager() 
->setService('zfcUserAuthentication', $authMock); // Error refers to this line. 

$ authMock nie jest najwyraźniej implementacja plugininterface, którą muszę zaimplementować, aby przejść do setService.

Czy $ authMock nie ma być tam przekazywane, ponieważ jest używane podczas testów jednostkowych? Czy powinienem używać innej metody setService (zorientowanej na testowanie jednostkowe)?

Potrzebuję sposobu na zajęcie się logowaniem do mojej aplikacji lub testowanie mojego urządzenia jest bezcelowe.

Dzięki za radę.

=== Edit (02.11.2013) ===

Chciałem skupić się na tej części wyjaśnienia, jak myślę, że jest to obszar problemu:

// Getting mock of authentication object, which is used as a plugin. 
$authMock = $this->getMock('ZfcUser\Controller\Plugin\ZfcUserAuthentication'); 

// Some expectations of the authentication service. 
$authMock -> expects($this->any()) 
    -> method('hasIdentity') 
    -> will($this->returnValue(true)); 

$authMock -> expects($this->any()) 
    -> method('getIdentity') 
    -> will($this->returnValue($ZfcUserMock)); 

// At this point, PluginManager disallows mock being assigned as plugin because 
// it will not implement plugin interface, as mentioned. 
$this -> controller->getPluginManager() 
->setService('zfcUserAuthentication', $authMock); 

Jeśli mock nie obsługuje koniecznych implementacji, jak inaczej mam udawać logowanie?

+0

Czy to prawda, że ​​nie jest to konieczne dla sterowników testujących urządzenia, ponieważ jest to model? Uważam, że to jest miejsce, w którym przechowuję cały mój kod uwierzytelniający. – Shoreline

+0

Zrobiłem coś podobnego niedawno bez problemu. Jak wygląda twoja pełna klasa testcase? Jak wygląda twój bootstrap testowy? I wreszcie akcja, którą próbujesz przetestować. – Ruben

+0

Czy korzystasz ze specjalnej konfiguracji aplikacji podczas testowania urządzenia? W takim przypadku moduł zfcUser nie jest ładowany w środowisku testowym. – SmasherHell

Odpowiedz

3

Masz problem z odstępem między nazwami lub autoloaderem.

Podczas tworzenia fałszywego dokumentu nie można znaleźć definicji klasy ZfcUser\Controller\Plugin\ZfcUserAuthentication. Tak więc PHPUnit tworzy próbę, która tylko rozszerza tę klasę dla twojego testu. Jeśli klasa była dostępna, PHPUnit użyje rzeczywistej klasy, aby rozszerzyć ją podczas próby, która następnie użyje rodzica class/interface.

Można zobaczyć tę logikę tutaj: https://github.com/sebastianbergmann/phpunit-mock-objects/blob/master/PHPUnit/Framework/MockObject/Generator.php

if (!class_exists($mockClassName['fullClassName'], $callAutoload) && 
     !interface_exists($mockClassName['fullClassName'], $callAutoload)) { 
     $prologue = 'class ' . $mockClassName['originalClassName'] . "\n{\n}\n\n"; 

     if (!empty($mockClassName['namespaceName'])) { 
      $prologue = 'namespace ' . $mockClassName['namespaceName'] . 
         " {\n\n" . $prologue . "}\n\n" . 
         "namespace {\n\n"; 

      $epilogue = "\n\n}"; 
     } 

     $cloneTemplate = new Text_Template(
      $templateDir . 'mocked_clone.tpl' 
     ); 

Więc jeśli nie ma klasy lub interfejsu, PHPUnit rzeczywiście utworzyć jeden się tak, że spotka się z makiety podpowiedzi typu oryginalnej nazwy klasy. Jednak żadne klasy nadrzędne ani interfejsy nie zostaną uwzględnione, ponieważ PHPUnit ich nie zna.

Powodem tego może być brak odpowiedniej przestrzeni nazw w teście lub problem z autoloaderem. Trudno powiedzieć, nie widząc całego pliku testowego.


Alternatywnie zamiast wyśmianie ZfcUser\Controller\Plugin\ZfcUserAuthentication, można mock Zend\Mvc\Controller\Plugin\PluginInterface w swoim badaniu i przekazać, że w menedżerze wtyczek. Chociaż jeśli masz podpowiedź typu dla wtyczki w swoim kodzie, test nadal nie będzie działał.

//Mock the plugin interface for checking authorization 
$authMock = $this->getMock('Zend\Mvc\Controller\Plugin\PluginInterface'); 

// Some expectations of the authentication service. 
$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); 
0

Właśnie zrobiłem przykład wtyczki FlashMessenger. Powinieneś po prostu użyć ControllerPluginManager, aby zastąpić ControllerPlugin. Upewnij się, że aplikacja bootstrap wywołuje setApplicationConfig();

<?php 
namespace SimpleTest\Controller; 

use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase; 

class SimpleControllerTest extends AbstractHttpControllerTestCase { 

    public function testControllerWillAddErrorMessageToFlashMessenger() 
    { 
     $flashMessengerMock = $this->getMockBuilder('\Zend\Mvc\Controller\Plugin\FlashMessenger', array('addErrorMessage'))->getMock(); 
     $flashMessengerMock->expects($this->once()) 
      ->method('addErrorMessage') 
      ->will($this->returnValue(array())); 


     $serviceManager = $this->getApplicationServiceLocator(); 
     $serviceManager->setAllowOverride(true); 
     $serviceManager->get('ControllerPluginManager')->setService('flashMessenger', $flashMessengerMock); 

     $this->dispatch('/error/message'); 

    } 
}?>