• Jeder User im Forum verpflichtet sich zur Akzeptanz und zur Einhaltung dieser Regeln:
    1. Umgangston
      Ein angemessener höflicher Umgangston, ohne Beleidigungen, Beschimpfungen und aggressive Postings ist für jedes Mitglied Pflicht.
    2. Beiträge
      Jedes Mitglied sollte sich bemühen nur sinnvolle Beiträge zum Thema zu posten. Dabei ist unbedingt vorher zu prüfen, ob das Thema vorher schon einmal diskutiert wurde und daher fortgesetzt werden kann
      • Suchfunktion benutzen!
      • offizielle Doku lesen!
    3. Haftung
      Jeder Beitragsersteller übernimmt die alleinige Verantwortung seiner Inhalte.
    4. Werbung
      Wir erlauben keine Beiträge, Signaturen, Private Nachrichten oder eMails an Benutzer, die Werbung enthalten. Ausgenommen
      sind Stellengesuche /-angebote, welche ausschließlich im Forum "Stellengesuche" veröffentlicht werden dürfen.
    5. Verstöße
      Regelwidrige Beiträge sollten dem Team gemeldet werden. Nach deren Überprüfung werden wir schnellstmöglich
      entsprechend handeln.
    6. Authorität
      Den Anweisungen der Team-Mitglieder (Administratoren und Moderatoren) sind in diesem Forum Folge zu leisten.
      Bei Fragen oder Beschwerden bitte an diese wenden.
    Wir möchten Euch darauf aufmerksam machen, dass es bei Verstößen gegen einen oder mehreren der oben genannten
    Punkte dem Team frei steht entsprechend zu handeln. Dies kann z.B. das Löschen eines Beitrags, das Ausschliessen bzw.
    Sperren von Mitgliedern oder aber lediglich eine Verwarnung sein.

    In diesem Zusammenhang sollte erwähnt werden, dass das Forum automatisch die IP-Adresse jedes Beitrag-Erstellers
    speichert. Bei schweren Vergehen, behalten wir es uns vor, die IP-Adresse zur Strafverfolgung weiterzugeben.
  • Willkommen im Zend Framework Forum

    ZF1 Zend Framework 1 + ZF2 Zend Framework 2

    Das Zend Framework Forum ist seit 2006 die erste Anlaufstelle für Zend Framework Entwickler in Deutschland. Mit über 70.000 Beiträgen und einer steigenden Nutzerzahl bietet das Forum hilfreiche Themen und ZF-Tutorials für professionelle Entwickler, fortgeschrittene Programmierer sowie Zend Framework Einsteiger.
    Wenn dies Dein erster Besuch in der Zend Framework Community ist, lies bitte zuerst die Hilfe - FAQ durch. Du musst Dich registrieren, bevor Du Beiträge verfassen kannst. Klicke oben auf 'Registrieren', um die Registrierung zu starten. Du kannst auch jetzt schon Beiträge lesen. Hier im Forum findest Du die Zend Framework Hilfe, die Du suchst!

    Grüße an alle Zend Framework Entwickler. Das Team vom Zend Framework Forum!

    Drupal Agentur

ZF2 PHPUnit PluginMock in Controller wird nicht gesetzt

Hiho,

nach vielen Stunden des Mockens und testens muss ich jetzt dochmal fragen.
Ich habe in meinem Projekt Controller die ein Plugin benutzen (User-Plugin, hat die Identity und so weiter).
In meinen Tests fällt mir dieses Plugin nun auf die Füße.
Das ist der Test der fehlschlägt:
PHP:
public function testIndexActionCanBeAccessed()
{    $this->dispatch('/statistic');
    $this->assertResponseStatusCode(200);
    $this->assertModuleName('Statistic');
    $this->assertControllerName('StatisticController');
    $this->assertControllerClass('StatisticController');
    $this->assertMatchedRouteName('statistic');
}
Der Fehler liegt daran, das das Plugin nicht ersetzt wird. Somit enthält $this->User() immer das echte Plugin statt mein Mock, das gemockte Plugin wird hier gesetzt:
PHP:
private function setUserPlugin()
{
    $authMock = self::getMockBuilder('Zend\Authentication\AuthenticationService')
        ->disableOriginalConstructor()
        ->setMethods(array('hasIdentity', 'getIdentity'))
        ->getMock();

    $authMock->expects(self::once())
        ->method('hasIdentity')
        ->will(self::returnValue(true));

    $authMock->expects(self::once())
        ->method('getIdentity')
        ->will(self::returnValue(array('userid' => 1)));
    $sm = \StatisticTest\Bootstrap::getServiceManager();
    $sm->setAllowOverride(true);
    $sm->setService('auth_service', $authMock);

    $this->setApplicationMapper();

    $pluginMock = self::getMockBuilder('Application\Controller\Plugin\UserPlugin')
        ->setMethods(array('__invoke', 'hasIdentity', 'getIdentity', 'getAuthService','setController', 'getController'))
        ->getMock();
    $pluginMock->expects(self::once())
        ->method('__invoke')
        ->will(self::returnValue($this->getUserMock()));
    $pluginMock->expects(self::once())
        ->method('hasIdentity')
        ->will(self::returnValue(true));
    $pluginMock->expects(self::once())
        ->method('getIdentity')
        ->will(self::returnValue(array('userid' => 1)));

    $sm->setAllowOverride(true);
    $sm->get('ControllerPluginManager')->setService('User', $pluginMock);
    $sm->setAllowOverride(false);
}
Die funktion "setUserPlugin" wird im setUp des Tests ausgeführt:
PHP:
public function setUp()
{
    $this->setUserPlugin();
    $this->setServiceMock();

    $this->controller = new \Statistic\Controller\StatisticController();
    parent::setUp();
}
Da ich nicht weiter weis bitte ich euch da mal um Hilfe.
Im Moment hab ich 2 Gedanken. Entweder wird das Plugin einfach nie gesetzt, was wahrscheinlich ist da alle Testausgaben im "echten" Plugin von PHPUnit ausgeführt werden oder ich hab irgendwas entscheidendes übersehen.
Bin euch auf jedenfall dankbar für jeden Ansatzpunkt
 

Kaiuwe

Super-Moderator
…oder ich hab irgendwas entscheidendes übersehen.
Mir kommt das alles viel zu kompliziert vor, einiges ist auch unklar und anderes macht mir Angst. ;)

PHP:
$sm = \StatisticTest\Bootstrap::getServiceManager();
Was ist das?

PHP:
$pluginMock = self::getMockBuilder('Application\Controller\Plugin\UserPlugin')
Warum ist dies notwendig?

PHP:
$this->setServiceMock();
Was ist das?


Um es mal kurz zu sagen: „zend-test“ initialisiert deine komplette Anwendung und damit auch dein Kontroller-Plugin. Wenn du jetzt den Status „eingeloggt“ durchspielen möchtest, dann reicht es doch aus, im Service-Manager den „Authentication-Service“ (AuthenticationServiceInterface) zu hinterlegen / überschreiben.

PHP:
$this->getApplicationServiceLocator()->setAllowOverride(true);
$this->getApplicationServiceLocator()->setService(
    \Zend\Authentication\AuthenticationServiceInterface::class,
    $myAuthenticationServiceMockObject
);
$this->getApplicationServiceLocator()->setAllowOverride(false);
Das Ganze kannst du als „Trait“ umsetzen und so in deinen Testklassen wiederverwenden. (etwas Hintergrund zur Thematik Traits in Tests)
 
Um deine Fragen zu beantworten:
Was ist das?
$sm = \StatisticTest\Bootstrap::getServiceManager();
Den ServiceManager aus der TestBootstrap laden. wird zum überschreiben von Services und Plugins genutzt.

Warum ist dies notwendig?
$pluginMock = self::getMockBuilder('Application\Controller\Plugin\UserPlugin')
Erstellt ein Mock des UserPlugins (das was ich in den ServiceManager schreiben will).

Warum ist dies notwendig?
$this->setServiceMock();

Setzt noch ein weiteres MockObjeckt ;)

Was ist das?

Config 'auth_service' heist der Service bei mir, ist noch etwas älter und wird nicht per Factory erstellt aber darum geht es ja nicht (vor dem Refactorn will ich erstmal Tests schreiben).
Im Endeffekt möchte ich sämtliche Abhängigkeiten der zu testenden Klasse mocken. Denn, ich will NUR diese Klasse testen und nicht noch anderen Code.
Daher auch die lustigen kleinen MockObjekte und MockBuilder.
 
Zuletzt bearbeitet:

Kaiuwe

Super-Moderator
Im Endeffekt möchte ich sämtliche Abhängigkeiten der zu testenden Klasse mocken. Denn, ich will NUR diese Klasse testen und nicht noch anderen Code.
Bitte verwechsle nicht die Unit-Tests mit den Integrationstests:

  1. Wie in deinem Ausgangsbeitrag zu entnehmen ist, verwendest du „zend-test“ und dies ist zum Testen deiner MVC-Anwendung. Also genau das Zusammenspiel der Klassen und Komponenten.
  2. Um Klassen einzelnen zu testen bleibt es beim „einfachen“ PHPUnit und hier musst du die Test-Double erstellen.
Du wirst gerade beides durcheinander!
 

Kaiuwe

Super-Moderator
Den ServiceManager aus der TestBootstrap laden. wird zum überschreiben von Services und Plugins genutzt.
Für die Verwendung von „zend-test“ wird keine Bootstrap-Datei oder Klasse benötigt. Siehe dazu in der Doku.
Den Service-Manager bekommst in deiner Testklasse über die Methode „getApplicationServiceLocator“.

Erstellt ein Mock des UserPlugins (das was ich in den ServiceManager schreiben will).
Bei Integrationstests nicht überschreiben, denn das Plugin soll innerhalb der Anwendung korrekt funktionieren.
Den Test für die einzelne Klasse solltest du bereits in einem Unit-Test abgewickelt haben.
 
Es handelt sich bei dem Test um einen UnitTest für den Controller, keinen Integrationstest. Hab ich auch nirgends geschrieben.
Sonst würde ich mir nciht die Mühe des Mockens machen.
Hab meinen Fehler auch gefunden, im Controller war der ServiceLocator nicht gefüllt.
 

Kaiuwe

Super-Moderator
Es handelt sich bei dem Test um einen UnitTest für den Controller, keinen Integrationstest. Hab ich auch nirgends geschrieben.
Dein Ausgangsbeitrag sagt aber etwas anders:
PHP:
public function testIndexActionCanBeAccessed()
{    $this->dispatch('/statistic');
    $this->assertResponseStatusCode(200);
    $this->assertModuleName('Statistic');
    $this->assertControllerName('StatisticController');
    $this->assertControllerClass('StatisticController');
    $this->assertMatchedRouteName('statistic');
}
Daher schloss ich darauf, dass es um Integrationstests geht.
 
Wenn man sich das stink normale Tutorial von ZF 2 anschaut ist das ein normaler Controllertest, der prüft ob die Action erreichbar ist. Gut kann man auch schon zu den Integrationstests zählen, ist aber sicher Ansichtssache.
 
Oben