ZF2 – Mocking-Dienst in Module.php angefordert

Lesezeit: 4 Minuten

Benutzer-Avatar
Marcosch

Ich versuche, einen Controller meiner ZF2-Anwendung zu testen. Angenommen, dieser Controller befindet sich in meiner A Modul.

In dem onBootstrap Methode der Module.php des Moduls A Ich verwende den Dienstmanager, um beispielsweise einen Dienst eines anderen Moduls abzurufen Bdass ich nicht lade.

Wie kann ein Mock des angeforderten Dienstes im Dienstmanager eingestellt werden? Beachten Sie, dass ich nicht verwenden kann $this->getApplicationServiceLocator() Dies in meinem Test zu tun, da dies bereits der Aufruf ist Module.onBootstrap Methode meiner A Modul.

Um etwas Code zu posten, das ist, was ich im Moment tue

bootstrap.php

namespace Application;

use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\ServiceManager\ServiceManager;
use RuntimeException;

class Bootstrap
{
    protected static $serviceManager;

    public static function init()
    {
        $modulePath = static::findParentPath('module');
        $vendorPath = static::findParentPath('vendor');

        if (is_readable($vendorPath . '/autoload.php')) {
            $loader = include $vendorPath . '/autoload.php';
        } else {
            throw new RuntimeException('Cannot locate autoload.php');
        }

        $config = [
            'modules' => [
                'Application',
            ],
            'module_listener_options' => [
                'module_paths' => [
                    $modulePath,
                    $vendorPath
                ]
            ]
        ];

        $serviceManager = new ServiceManager(new ServiceManagerConfig());
        $serviceManager->setService('ApplicationConfig', $config);
        $serviceManager->get('ModuleManager')->loadModules();
        static::$serviceManager = $serviceManager;
    }

    protected static function findParentPath($path)
    {
        $dir = __DIR__;
        $previousDir=".";
        while (!is_dir($dir . "https://stackoverflow.com/" . $path)) {
            $dir = dirname($dir);
            if ($previousDir === $dir) {
                return false;
            }
            $previousDir = $dir;
        }
        return $dir . "https://stackoverflow.com/" . $path;
    }

    public static function getServiceManager()
    {
        return static::$serviceManager;
    }
}

Bootstrap::init();

meine eigentliche Testklasse

namespace Application\Functional;

use Application\Bootstrap;

use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;

class ValidateCustomerRegistrationTest extends AbstractHttpControllerTestCase
{
    public function setUp()
    {
        $serviceManager = Bootstrap::getServiceManager();
        $applicationConfig = $serviceManager->get('ApplicationConfig');

        $this->setApplicationConfig($applicationConfig);
        parent::setUp();
    }

    public function testRegisterValidUserWithOnlyEquomobiliData()
    {
        $this->getApplicationServiceLocator();
    }
}

Module.php vereinfacht

namespace Application

Class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $serviceManager = $e->getApplication()->getServiceManager();
        $service = $serviceManager->get('Service\From\Other\Module');
    }
}

  • Was versuchst du zu testen? Wenn Sie Schwierigkeiten haben, Objekte zu verspotten, ist dies im Allgemeinen ein Zeichen für schlechte Architektur. Brauchen Sie Module wirklich, um sich auf diesen Dienst zu verlassen? Kann man es woanders spritzen?

    – Chaos0815

    26. April 2016 um 7:08 Uhr

  • Können Sie die vollständige Klassendefinition des Controllers teilen, den Sie testen?

    – Welke

    13. Mai 2016 um 11:56 Uhr

Es gibt hier nicht genügend Daten, um Ihnen direkt zu helfen. Sinnvoller wäre die Funktion ValidateCustomerRegistrationTest->getApplicationServiceLocator() für Starter.

Ich hoffe ich konnte dir indirekt helfen.

Refactoring könnte hilfreich sein

Wenn ich einen Unit-Test schreibe, beginne ich mit ein paar persönlichen Regeln.

Testen Sie nur den zu testenden Code. Mock ALLES andere. Es ist nicht nötig, etwas zu testen, das bereits eigene Tests haben sollte.

Wie eine Funktion funktioniert, sollte nicht wichtig sein. Nur die Ein-/Ausgabe. Dadurch bleibt Ihr Test auch dann funktionsfähig, wenn sich der Kern einer Funktion drastisch ändert.

/**
 * @param MvcEventInterface $event
 * @param Service\From\Other\ModuleInterface $service
 *
 * @return boolean
 */
public function onBootstrap(MvcEventInterface $event, Service\From\Other\ModuleInterface $service)
{
  return true;
}

Dann in der Testklasse:

public function testOnBootstrap(){
   $eventMock = $this->getMock(MvcEventInterface::class);
   $serviceMock = $this->getMock(ModuleInterface::class);
   $module = new Module();

   $result = $module->onBootstrap($eventMock, $serviceMock);
   $this->assertTrue($result);
}

* Ich weiß eindeutig nicht, was Sie zu testen versuchen

Wenn Refactoring keine Option ist

Es gibt zwei Mock-Typen, die helfen können und die mir auf Anhieb einfallen: Mock und MockBuilder. Werfen Sie einen Blick auf die PHPUnit-Dokumentation für Test Doubles.

$serviceMock = $this->getMockBuilder(ServiceManager::class)
    ->disableOriginalConstructor()
    ->setMethods([
        '__construct',
        'get'
    ])
    ->getMock();

$serviceMock
    ->expects($this->any())
    ->method('get')
    ->will($this->returnValue(
        $this->getMock(ModuleManagerInterface::class)
    ));

Viel Glück, ich hoffe, Sie können uns wissen lassen, ob wir Ihnen genauer helfen können. Ich würde auch empfehlen, sich mit den SOLID-Prinzipien der objektorientierten Programmierung zu befassen. Eines von vielen Programmierprinzipien, die Ihren Code sauber, einfach erweiterbar und einfach zu testen machen sollten.

Eine Möglichkeit, das zu erreichen, was Sie versuchen, kann darin bestehen, ein Überschreiben des Diensts im Dienst-Manager zu erzwingen, bevor die Anforderung an Ihren Testcontroller gesendet wird. Hier ist ein Beispiel wie man das macht (HINWEIS: Da das, was Sie tun, während des Bootstrap-Prozesses des Moduls stattfindet, lässt sich das Beispiel möglicherweise nicht zu 100 % auf Ihre Situation übertragen).

Wie andere Leute bereits erwähnt haben: Vielleicht möchten Sie noch einmal überprüfen, ob Sie auf einen saubereren Ansatz umgestalten können, aber das ist eine andere Geschichte.

Um den Service Manager und die damit getätigten Anrufe zu verspotten, können Sie Spott verwenden https://github.com/mocky/mocky. Diese Bibliothek ist Framework-unabhängig, also sollte es funktionieren, selbst wenn Sie PHPUnit oder ein anderes Tool verwenden.

Der schlechte Weg, es zu verwenden, aber es sollte Ihr Problem schnell lösen, besteht darin, die Klasse mit mockery(‘overload:myclass’) zu überladen, wobei myclass die Instanz des Dienstmanagers ist.

Die beste Möglichkeit, die Bibliothek zu verwenden, besteht darin, das Abhängigkeitsinjektionsmuster zu verwenden, um einen Schein des Service Locators einzufügen, wenn Ihre App für die Tests Boostraping durchführt.

Eine einfache Möglichkeit, damit umzugehen, besteht darin, eine Fabrik für Ihren Controller zu erstellen und den Service Locator darin einzufügen. Dann initiieren Sie in Ihrem Test den Controller mit dem Mock des Service Locators als Parameter.

1216670cookie-checkZF2 – Mocking-Dienst in Module.php angefordert

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy