2013-02-10 22 views
23

stavo imparando di test di unità e ho tentato di risolvere il seguente problema:ZF2 autenticazione unit test

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

... con l'unica risposta data a:

Simple ZF2 Unit Tests for a controller using ZfcUser

Così la mia funzione setUp ha lo stesso aspetto. Purtroppo, ricevo il seguente messaggio di errore:

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

E 'causata in questa parte del codice (divisi nel mio codice nello stesso modo): oggetto

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

Il $ authMock non è a quanto pare implementazione dell'interfaccia plugin, che devo implementare per passare in setService.

$ authMock non è destinato a essere passato lì per il suo utilizzo nei test di unità? Dovrei usare un metodo setService diverso (unit-testing oriented)?

Ho bisogno di un modo per gestire la registrazione nella mia applicazione, o il mio test delle unità è inutile.

Grazie per qualsiasi consiglio.

=== Edit (2013/11/02) ===

ho voluto concentrarmi su questa parte di chiarimenti, come credo che questo sia l'area del problema:

// 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); 

Se il il mock non gestisce le implementazioni necessarie, in quale altro modo posso fingere di accedere?

+0

Sono corretto non è tanto necessario per i controllori dell'unità di test in quanto è modelli? Trovo che è dove tengo tutto il mio codice di autenticazione. – Shoreline

+0

Ho fatto qualcosa di simile di recente senza alcun problema. Che aspetto ha la tua classe di testcase completa? Che aspetto ha il tuo bootstrap di prova? E infine l'azione che stai cercando di testare. – Ruben

+0

Si utilizza una configurazione di applicazione speciale durante il test dell'unità? In questo caso è possibile che il modulo zfcUser non sia caricato nell'ambiente di test. – SmasherHell

risposta

3

Si è verificato un problema con la spaziatura dei nomi o il caricatore automatico.

Quando si crea il proprio simulato, la definizione di classe di ZfcUser\Controller\Plugin\ZfcUserAuthentication non viene trovata. Quindi PHPUnit crea una simulazione che estende solo questa classe per il tuo test. Se la classe fosse disponibile allora PHPUnit userà la classe effettiva per estenderla durante la simulazione, che utilizzerà quindi le classi/interfacce padre.

Si può vedere questa logica qui: 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' 
     ); 

Quindi, se non v'è alcuna classe o interfaccia, PHPUnit sarà effettivamente creare una stessa in modo che il mock incontrerà il tipo di hinting nome della classe originale. Tuttavia, eventuali classi o interfacce genitore non saranno incluse perché PHPUnit non ne è a conoscenza.

Ciò sarebbe dovuto al fatto di non includere lo spazio dei nomi corretto nel test o di avere un problema nel caricatore automatico. È difficile dirlo senza vedere effettivamente l'intero file di test.


In alternativa, piuttosto che beffardo ZfcUser\Controller\Plugin\ZfcUserAuthentication, si potrebbe prendere in giro il Zend\Mvc\Controller\Plugin\PluginInterface nel test e passare che nel plugin manager. Sebbene tu stia suggerendo di digitare il plugin nel tuo codice, il tuo test non funzionerà.

//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

Ho appena creato un esempio per il plug-in FlashMessenger. Dovresti semplicemente usare ControllerPluginManager per sovrascrivere ControllerPlugin. Assicurati che il bootstrap dell'applicazione chiami 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'); 

    } 
}?>