ZF2 – Servicio de simulación solicitado en Module.php

5 minutos de lectura

avatar de usuario
marcosh

Estoy tratando de probar un controlador de mi aplicación ZF2. Supongamos que este controlador está en mi A módulo.

En el onBootstrap metodo de la Module.php del módulo A Estoy usando el administrador de servicios para recuperar un servicio de otro módulo, digamos Bque no estoy cargando.

¿Cómo se puede configurar un simulacro del servicio solicitado en el administrador de servicios? Mente que no puedo usar $this->getApplicationServiceLocator() para hacer esto en mi prueba, ya que esto ya está llamando al Module.onBootstrap método de mi A módulo.

Para publicar algo de código, esto es lo que estoy haciendo en este momento.

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

mi clase de prueba real

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();
    }
}

Módulo.php simplificado

namespace Application

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

  • ¿Qué estás tratando de probar? En general, si tiene problemas para burlarse de los objetos, es un signo de mala arquitectura. ¿Realmente necesita que Module dependa de ese servicio? ¿Se podría inyectar en otro lugar?

    – caos0815

    26 de abril de 2016 a las 7:08

  • ¿Puede compartir la definición de clase completa del controlador que está probando?

    – Marchitez

    13 mayo 2016 a las 11:56

No hay suficientes datos aquí para ayudarte directamente. Sería más útil tener la función ValidateCustomerRegistrationTest->getApplicationServiceLocator() para principiantes.

Espero poder ayudarte indirectamente.

La refactorización podría ser útil

Cuando estoy escribiendo una prueba de unidad, comienzo con algunas reglas personales.

Solo prueba el código que se está probando. Burlarse de TODO lo demás. No es necesario probar algo que ya debería tener sus propias pruebas.

Cómo funciona una función no debería ser importante. Solo la entrada/salida. Esto mantiene su prueba viable incluso cuando el núcleo de una función cambia drásticamente.

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

Luego en la clase de prueba:

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

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

* Claramente no sé lo que estás tratando de probar

Cuando la refactorización no es una opción

Hay dos tipos de simulacros que pueden ayudar, que se me ocurren de inmediato, simulacros y simulacros. Echa un vistazo a la Documentación de PHPUnit para 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)
    ));

Buena suerte, espero que nos puedas decir si podemos ayudarte más específicamente. También recomendaría investigar los Principios SOLID de la Programación Orientada a Objetos. Uno de los muchos principios de programación que deberían hacer que su código sea limpio, fácil de extender y fácil de probar.

Una forma de lograr lo que intenta hacer puede ser forzar una sobrescritura del servicio en el Administrador de servicios antes de enviar la solicitud a su controlador de prueba. aquí hay un ejemplo de cómo hacerlo (NOTA: dado que lo que está haciendo es durante el proceso de arranque del módulo, es posible que el ejemplo no se traduzca al 100% en su situación).

Como mencionaron otras personas: es posible que aún desee verificar dos veces si puede refactorizar a un enfoque más limpio, pero esa es otra historia.

Para simular el administrador de servicios y las llamadas realizadas con él, puede usar la burla https://github.com/mockery/mockery. Esta biblioteca es independiente del marco, por lo que incluso si usa PHPUnit u otra herramienta, debería funcionar.

La mala manera de usarlo, pero debería resolver su problema rápidamente, es sobrecargar la clase usando burla (‘overload: myclass’) donde myclass es la instancia del administrador de servicios.

La mejor manera de usar la biblioteca es usar el patrón de inyección de dependencia para inyectar un simulacro del localizador de servicios cuando su aplicación está mejorando para las pruebas.

Una manera fácil de hacerlo es crear una fábrica para su controlador, inyectar el localizador de servicios dentro. Luego, en su prueba, inicia el controlador con el simulacro del localizador de servicios como parámetro.

¿Ha sido útil esta solución?