2017-04-08 231 views
0

如何使用實體管理器測試我的工廠?我有一個錯誤,因爲我需要讓我的容器返回一個從doctrine創建的類的實例(我甚至不知道返回的是什麼)。如何對依賴關係的工廠進行單元測試

如何創建一個測試,我可以通過?

// factory i want to test 
public function __invoke(ContainerInterface $container, $requestedName, array $options = null) 
{ 
    $googleAppOption = $container->get(GoogleAppOptions::class); 
    $em = $container->get('doctrine.entity_manager.orm_default'); 
    return new GoogleTokenHandler($googleAppOption, new GoogleTokenClient(), $em); 
} 

//test function 
public function testReturnsTokenHandlerInstance() 
{ 
    $googleOptionsFactory = new GoogleOptionsFactory(); 
    $googleOptions = $googleOptionsFactory($this->container->reveal(), null); 
    $this->container->get(GoogleAppOptions::class)->willReturn($googleOptions); 
    $googleTokenHandlerFactory = new GoogleTokenHandlerFactory($this->container); 
    $tokenHandler = $googleTokenHandlerFactory($this->container->reveal(), null); 
    $this->assertInstanceof(GoogleTokenHandler::class, $tokenHandler); 

} 

回答

1

事實上,這是很難測試是一個很好的跡象,有這樣的臭味。在你的情況下,顯然是容器被注入,然後被用來定位服務。我建議重寫這個類,以在構造函數中注入OptionsFactory(或者更好的選項)和EntityManager以及動態創建的GoogleClient。你將在到達是一個調用這幾乎是這樣的:

return new GoogleTokenHandler(
    $this->optionsFactory, 
    $this->tokenClient, 
    $this->entityManager 
); 

正如你可以看到你既不使用也不$requestedName可選$options傳遞給你的__invoke。這有點奇怪,但這不會影響我們的測試。現在,你可以簡單地在您的測試模擬出的服務和檢查調用是否返回正確的實例:

public function testFactoryInvokeReturnsInstance() 
{ 
    $optionsFactory = $this->prophesize(OptionsFactory::class); 
    $tokenClient = $this->prophesize(GoogleTokenClient::class); 
    $entityManager = $this->prophesize(EntityManager::class); 

    $factory = new MyFactory(
     $optionsFactory->reveal(), 
     $tokenClient->reveal(), 
     $entityManager->reveal() 
    ); 

    $this->assertInstanceOf(GoogleTokenHandler::class, $factory->__invoke()); 
    // Alternatively you can use the __invoke-magic directly: 
    $this->assertInstanceOf(GoogleTokenHandler::class, $factory()); 
} 

你可以做同樣的你的類,但基本上你將不得不增加一個集裝箱,然後踩滅了get - 從中​​獲取所有服務的方法。例如,您在代碼段中缺少實體管理器。如果您的方法中創建的GoogleTokenClient需要一些參數/選項,則無法嘲笑該行爲,事實上,如果不更改代碼,您將無法將其切換出去。而通過將其注入構造函數中,您可以重新配置您的容器以傳入不同的對象。

留給後人,您的完整的工廠可能會是這個樣子:

class Factory { 
    private $optionsFactory; 
    private $tokenClient; 
    private $entityManager; 

    public function __construct(GoogleTokenClient $tokenClient, ...) 
    { 
     $this->tokenClient = $tokenClient; 
     ... 
    } 

    public function __invoke() { return new GoogleTokenHandler(...); } 
} 
+0

但是這意味着我需要一個工廠來創建我的工廠? –

+0

或者我應該使用我的控制器中的服務定位器來創建我的TokenHanlderFactory? –

+0

我沒有看到哪個工廠會創建工廠,但一般來說 - 如果涉及到某種動態性 - 有一個「FactoryFactory」會很有用,而在Java代碼庫中它有點常見(當某些狀態在請求之間保持時) 。在你的控制器中,你可以使用容器來獲得工廠,或者用一些DI容器直接獲得TokenHandler,例如與Symfony的容器:https://symfony.com/doc/current/service_container/factories.html – dbrumann

相關問題