2013-02-10 23 views
23

Ich war über Unit-Tests lernen, und ich versuchte, das folgende Problem zu lösen:ZF2 Unit-Testing-Authentifizierung

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

... gegeben, die einzige Antwort, unter Verwendung von:

Simple ZF2 Unit Tests for a controller using ZfcUser

So Meine Setup-Funktion sieht gleich aus. Leider erhalte ich die Fehlermeldung:

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

Es wird in diesem Teil des Codes verursacht (aufgeteilt auf die gleiche Art und Weise in meinem Code nach oben):

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

Das Objekt $ authMock ist offenbar nicht Implementieren von plugininterface, das ich implementieren muss, um in setService zu übergeben.

Ist $ authMock nicht für die Verwendung in Komponententests gedacht? Sollte ich eine andere (Unit-Testing-orientierte) setService-Methode verwenden?

Ich brauche eine Möglichkeit, die Anmeldung in meiner Anwendung zu behandeln, oder meine Unit-Tests sind sinnlos.

Vielen Dank für jeden Hinweis.

=== Edit (11.02.2013) ===

Ich wollte auf diesem Teil zur Klärung konzentrieren, wie ich denke, das ist das Problem Bereich ist:

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

Wenn die Mock kümmert sich nicht um notwendige Implementierungen, wie sonst soll ich mich anmelden?

+0

Ich korrigiere, dass es nicht notwendig ist, Controller zu testen, wie es Modelle sind? Ich finde, dass ich dort meinen gesamten Authentifizierungscode aufbewahre. – Shoreline

+0

Ich habe kürzlich etwas Ähnliches ohne Probleme gemacht. Wie sieht Ihre komplette Testfallklasse aus? Wie sieht dein Test Bootstrap aus? Und schließlich die Aktion, die Sie versuchen zu testen. – Ruben

+0

Verwenden Sie eine spezielle Anwendungskonfiguration beim Komponententest? In diesem Fall ist es möglich, dass das zfcUser-Modul nicht in der Testumgebung geladen ist. – SmasherHell

Antwort

3

Sie haben ein Problem mit dem Namen-Abstand oder Ihrem Autoloader. Wenn Sie Ihren Mock erstellen, wird die Klassendefinition ZfcUser\Controller\Plugin\ZfcUserAuthentication nicht gefunden. Also erstellt PHPUnit einen Mock, der nur diese Klasse für Ihren Test erweitert. Wenn die Klasse verfügbar war, wird PHPUnit die tatsächliche Klasse verwenden, um sie zu erweitern, wenn sie ihren Schein erstellt, der dann die Elternklassen/Schnittstellen verwendet.

können Sie diese Logik siehe hier: 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' 
     ); 

Also, wenn es keine Klasse oder Schnittstelle ist, PHPUnit wird man sich tatsächlich schaffen, so dass der mock die Type Hinting von Original-Klassennamen entspricht. Alle übergeordneten Klassen oder Schnittstellen werden jedoch nicht berücksichtigt, da PHPUnit diese nicht kennt.

Dies liegt daran, dass der richtige Namespace in Ihrem Test nicht enthalten ist oder ein Problem in Ihrem Autoloader vorliegt. Es ist schwierig zu sagen, ohne tatsächlich die gesamte Testdatei zu sehen.


Alternativ statt ZfcUser\Controller\Plugin\ZfcUserAuthentication spöttisch, könnten Sie die Zend\Mvc\Controller\Plugin\PluginInterface in Ihrem Test verspotten und dass in den Plugin-Manager übergeben. Wenn Sie jedoch für das Plug-In in Ihrem Code tippen, funktioniert Ihr Test immer noch nicht.

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

Ich habe gerade ein Beispiel für die Flashmessenger-Plugin. Sie sollten nur den ControllerPlugManager verwenden, um das ControllerPlugin zu überschreiben. Stellen Sie sicher, dass die Anwendung Bootstrap ruft 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'); 

    } 
}?>