プロジェクトをSymfony 4に移行しています。私のテストスイートでは、機能テストにPHPUnitを使用しました(つまり、エンドポイントを呼び出して結果を確認しています)。多くの場合、さまざまなステップをチェックするためにサービスを模擬します。
Symfony 4に移行したので、この問題に直面しています:_Symfony\Component\DependencyInjection\Exception\InvalidArgumentException: The "my.service" service is already initialized, you cannot replace it.
_これを次のように再定義すると:static::$container->set("my.service", $mock);
テストの場合のみ、この問題をどのように修正できますか?
ありがとうございました
最後に、私は解決策を見つけました。多分最高ではないかもしれませんが、それはうまくいきます:
別のテストコンテナークラスを作成し、Reflectionを使用してservices
プロパティをオーバーライドします。
<?php
namespace My\Bundle\Test;
use Symfony\Bundle\FrameworkBundle\Test\TestContainer as BaseTestContainer;
class TestContainer extends BaseTestContainer
{
private $publicContainer;
public function set($id, $service)
{
$r = new \ReflectionObject($this->publicContainer);
$p = $r->getProperty('services');
$p->setAccessible(true);
$services = $p->getValue($this->publicContainer);
$services[$id] = $service;
$p->setValue($this->publicContainer, $services);
}
public function setPublicContainer($container)
{
$this->publicContainer = $container;
}
Kernel.php:
<?php
namespace App;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
class Kernel extends BaseKernel
{
use MicroKernelTrait;
public function getOriginalContainer()
{
if(!$this->container) {
parent::boot();
}
/** @var Container $container */
return $this->container;
}
public function getContainer()
{
if ($this->environment == 'prod') {
return parent::getContainer();
}
/** @var Container $container */
$container = $this->getOriginalContainer();
$testContainer = $container->get('my.test.service_container');
$testContainer->setPublicContainer($container);
return $testContainer;
}
それは本当に醜いですが、それは働いています。
置換はSymfony 3.3から廃止されました。サービスを置き換える代わりに、エイリアスを使用してみてください。 http://symfony.com/doc/current/service_container/alias_private.html
また、このアプローチを試すことができます:
$this->container->getDefinition('user.user_service')->setSynthetic(true);
を実行する前に$container->set()
私はこのようなテストをいくつか持っています(実際のコードはいくつかのアクションを実行して結果を返します。test-versionはすべての回答に対してfalseを返します)。
環境ごとにカスタム構成を作成して使用し(例:services_test.yaml、またはSymfony4の場合はおそらくtest/services.yaml)、最初にdev/services.yamlを含めてから、必要なサービスをオーバーライドする場合、最後の定義が使用されます。
app/config/services_test.yml:
imports:
- { resource: services.yml }
App\BotDetector\BotDetectable: '@App\BotDetector\BotDetectorNeverBot'
# in the top-level 'live/prod' config this would be
# App\BotDetector\BotDetectable: '@App\BotDetector\BotDetector'
ここでは、サービス名としてインターフェイスを使用していますが、 '@ service.name'スタイルでも同様に機能します。