2017-07-14 8 views
1

私の単体テストをうまく動作させるには問題があります。工場で作成されたサービスを使用するコントローラをテストしています。私が達成したいのは、アクティブなデータベース接続を使用せずにテストを実行できるように、工場を模擬サービスに置き換えることです。私のサービスマネージャの設定ファイルでオブジェクト・モッキング - 工場をサービス・マネージャーのサービスに置き換えるにはどうすればいいですか?

セットアップ

私は工場を指します。 工場では、ユニットテスト中に使用したくないアクティブなデータベース接続が必要です。

Namespace MyModule; 

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

注:私は、説明のためにクラス名と単純化された構成を変更しました。

このサービスでは、状況に関連しないため、私が今は行っていないマッパーを使用しています。マッパーは、独自のテストケースでテストされます。サービス自体にはそれ自身のテストケースもありますが、コントローラのアクションが動作するためには存在する必要があります。

コントローラアクションは、単にサービスから情報を受信します。

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

注:再び、私は、クラス名を変更し、説明のための例を簡略化しています。私はこのような目的の工場を上書きしようとした私は私のテストケースでは

を試してみた

/** 
* @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); 
} 

一見これは偉大に見えますが、それは動作しません。ファクトリをサービスマネージャのサービスリストに追加するだけで、ファクトリをオーバーライドしないようです。

$services->setServiceから$services->setFactoryに変更すると、別の工場を建設する必要があります。私ができることは、モックマッパーをサービスに挿入するファクトリを作成することですが、それは間違っていると感じます。私はサービスやマッパーではなく、コントローラをテストしています。そのような複雑なソリューションを避けようとしているので、テストケースを簡単かつ明確に保ちます。

私の状況に関するオプションはありますか?サービスマネージャーのサービスでファクトリをオーバーライドすることは可能ですか、それとも間違っているのですか?

+0

私は「別の工場を建設する」ことはできません。私は明らかにそれのクロージャを書くことができるので、これは必要ではありません。 –

答えて

1

私はユニットテストのために別の設定ファイルが必要だと思います。

設定/ phpunit.config:

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は、ユニットテスト用に作成した設定ファイルです。config/phpunit/yourfile.local.phpでは、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 => ... 
     ] 
    ) 
]; 

あなたも閉鎖、MyService::classはあなたが好きなことさせることができます。

+1

この解決策は、デフォルトconfigとunit test configを 'config/phpunit.config.php'ファイルにマージします。最初の行にはアプリの設定ファイルが含まれています。 – akond

+0

このソリューションは、テストスイートを実行するためにデフォルトのサービス/ファクトリ/モデルを置き換えなければならない状況でうまく動作します。しかし、1つのテストケースで、通常よりも約束が異なる特定のモックが必要な場合、この解決法は機能しません。 –

0

このために新しい工場を建設する必要はありません。

/** 
* @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); 
} 

これで、依然として必要なサービスのみを模擬することができます。テストケースに期待を追加することは、それほどフレキシブルです。

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

    $result = $this->service->findAll(); 
    $this->assertSame($expected, $result); 
} 
関連する問題