2017-07-14 1 views
1

J'ai de la difficulté à faire fonctionner mon test d'unité. Je suis en train de tester un contrôleur qui utilise un service créé par une usine. Ce que je veux accomplir est de remplacer une usine avec un service simulé afin que je puisse effectuer des tests sans employer une connexion de base de données active.Object Mocking - Comment remplacer une usine par un service dans le gestionnaire de service?

La configuration

Dans le fichier de configuration de mon gestionnaire de services Je signale une usine. L'usine nécessite une connexion à la base de données active que je ne souhaite pas utiliser pendant mon test unitaire.

Namespace MyModule; 

return [ 
    'factories' => [ 
     MyService::class => Factory\Service\MyServiceFactory::class, 
    ], 
]; 

Note: J'ai changé les noms de classe et une configuration simplifiée à des fins d'illustration.

Le service utilise un mappeur que je ne vais pas utiliser maintenant car cela ne correspond pas à la situation. Les cartographes sont testés dans leurs propres cas de test. Le service lui-même possède son propre boîtier de test, mais il doit être présent pour que les actions du contrôleur fonctionnent.

L'action du contrôleur reçoit simplement des informations du service.

Namespace MyModule\Controller; 

use MyModule\Service\MyService; 
use Zend\Mvc\Controller\AbstractActionController; 

class MyController extends AbstractActionController 
{ 
    /** 
    * @var MyService 
    */ 
    private $service; 

    /** 
    * @param MyService $service 
    */ 
    public function __construct(MyService $service) 
    { 
     $this->service = $service; 
    } 


    /** 
    * Provides information to the user 
    */ 
    public function infoAction() 
    { 
     return [ 
      'result' => $this->service->findAll(), 
     ]; 
    } 
} 

Note: Encore une fois, je l'ai changé les noms de classe et de simplifier l'exemple à des fins d'illustration.

Ce que j'ai essayé

Dans mon testcase j'ai essayé de passer outre le comme cette usine désirée:

/** 
* @return \Prophecy\Prophecy\ObjectProphecy|MyService 
*/ 
private function mockService() 
{ 
    $service = $this->prophesize(MyService::class); 
    $service->findAll()->willReturn(['foo', 'bar']); 

    return $service; 
} 

/** 
* @param \Zend\ServiceManager\ServiceManager $services 
*/ 
private function configureServiceManager(ServiceManager $services) 
{ 
    $services->setAllowOverride(true); 
    $services->setService(MyService::class, $this->mockService()->reveal()); 
    $services->setAllowOverride(false); 
} 

À première vue, cela ressemble beaucoup, mais il ne fonctionne pas. Il semble juste ajouter le service à la liste des services du gestionnaire de service, ne pas surcharger l'usine. Changement $services->setService à $services->setFactory exige de moi de construire une autre usine. Ce que je peux faire, c'est créer une usine qui injecte un simulateur dans le service, mais cela ne va pas. Je teste le contrôleur, pas le service ou le mappeur, alors j'essaie d'éviter des solutions complexes comme celles-ci pour que mes cas de test restent simples et clairs.

Existe-t-il des options concernant ma situation? Est-il possible de remplacer une usine par un service dans le gestionnaire de service ou est-ce que je regarde mal?

+0

Je déclare dans ma question que je ne suis pas prêt à "construire une autre usine". Ce n'est pas nécessaire puisque je suis évidemment capable d'écrire une fermeture pour cela. –

Répondre

1

Je pense que vous avez besoin d'un fichier de configuration séparé pour les tests unitaires.

phpunit.xml

<?xml version="1.0"?> 
<phpunit bootstrap="./Bootstrap.php"> 

Bootstrap.php

require 'vendor/autoload.php'; 
$configuration = include 'config/phpunit.config.php'; 

Zend\Mvc\Application::init ($configuration); 

config/phpunit.config.php est un fichier de configuration créé pour les tests unitaires seulement:

config/phpunit.config.php

$configuration = include (__DIR__ . '/application.config.php'); 
$configuration ['module_listener_options'] ['config_glob_paths'] [] = 'config/phpunit/{,*.}local.php'; 

config/PHPUnit/yourfile.local.php

return [ 
    'service_manager' => array (
     'factories' => [ 
      MyService::class => ... 
     ] 
    ) 
]; 

En config/phpunit/yourfile.local.php vous pouvez laisser MyService::class être tout ce que vous voulez, même une fermeture.

+1

Cette solution fusionne config par défaut avec unit test config dans le fichier 'config/phpunit.config.php': la première ligne contient le fichier de configuration de l'application. – akond

+0

Cette solution fonctionne correctement dans les situations où les services/usines/modèles par défaut doivent être remplacés pour qu'une suite de tests puisse être exécutée. Si, cependant, un seul test nécessite un simulacre spécifique avec des promesses différentes de l'habituel, cette solution ne fonctionnera pas et ma question est toujours valable. –

0

Il n'est pas nécessaire de construire de nouvelles usines pour cela. Il suffit d'utiliser une simple fermeture à la place:

/** 
* @param \Zend\ServiceManager\ServiceManager $services 
*/ 
private function configureServiceManager(ServiceManager $services) 
{ 
    $services->setAllowOverride(true); 

    $mockedService = $this->mockService(); 
    $services->setFactory(MyService::class, function() use ($mockedService) { 
     $mockedService->reveal(); 
    }); 

    $services->setAllowOverride(false); 
} 

Vous ne pouvez toujours vous moquer que du service requis. Ajouter des attentes dans le scénario de test est toujours aussi flexible qu'il devrait l'être:

public function testMyCase() 
{ 
    $expected = ['foo', 'bar']; 
    $this->mockService()->findAll()->willReturn($expected); 

    $result = $this->service->findAll(); 
    $this->assertSame($expected, $result); 
}