2017-07-14 52 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.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僅用於單元測試創​​建的配置文件:

配置/ phpunit.config。PHP

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

配置/ PHPUnit的/ yourfile.local.php

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

config/phpunit/yourfile.local.php可以讓MyService::class是任何你想要的,甚至倒閉。

+1

該解決方案將文件'config/phpunit.config.php'中的默認配置與單元測試配置合併:第一行包含應用程序的配置文件。 – akond

+0

該解決方案適用於需要替換測試套件才能運行的默認服務/工廠/模型的情況。但是,如果只有一個測試用例需要特殊的模擬,而且通常會有不同的承諾,這種解決方案將不起作用,我的問題依然存在。 –

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); 
} 
相關問題