如何在 symfony 4 上使用 PHPUnit 测试依赖注入
How to test dependency injection with PHPUnit on symfony 4
在一个symfony 4项目中,很多services/controllers需要日志。
尝试利用 symfony 提供的特征和自动装配选项的优势,我创建了一个将传递给不同服务的 loggerTrait。
namespace App\Helper;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
trait LoggerTrait
{
/** @var LoggerInterface */
private $logger;
/** @var array */
private $context = [];
/**
* @return LoggerInterface
*/
public function getLogger(): LoggerInterface
{
return $this->logger;
}
/**
* @required
*
* @param LoggerInterface|null $logger
*/
public function setLogger(?LoggerInterface $logger): void
{
$this->logger = $logger;
}
public function logDebug(string $message, array $context = []): void
{
$this->log(LogLevel::DEBUG, $message, $context);
}
...
}
(灵感来自 symfonycasts.com)
服务将使用此特征
namespace App\Service;
use App\Helper\LoggerTrait;
class BaseService
{
use LoggerTrait;
/** @var string */
private $name;
public function __construct(string $serviceName)
{
$this->name = $serviceName;
}
public function logName()
{
$this->logInfo('Name of the service', ['name' => $this->name]);
}
}
它完美运行,但我无法成功测试它。
我试图在测试中扩展 KernelTestCase 以模拟 loggerInterface,但我收到 Symfony\Component\DependencyInjection\Exception\InvalidArgumentException:"Psr\Log\LoggerInterface" 服务是私有的,您无法替换它完美的感觉。
这是我的测试:
namespace App\Tests\Service;
use App\Service\BaseService;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class BaseServiceTest extends KernelTestCase
{
private function loggerMock()
{
return $this->createMock(LoggerInterface::class);
}
protected function setUp()
{
self::bootKernel();
}
/**
* @test
* @covers ::logName
*/
public function itShouldLogName()
{
// returns the real and unchanged service container
$container = self::$kernel->getContainer();
// gets the special container that allows fetching private services
$container = self::$container;
$loggerMock = $this->loggerMock();
$loggerMock->expect(self::once())
->method('log')
->with('info', 'Name of the service', ['name' => 'service_test']);
$this->logger = $container->set(LoggerInterface::class, $loggerMock);
$baseService = new BaseService('service_test');
var_dump($baseService->getLogger());
}
}
是否有在服务内部测试此类记录器的解决方案?
您可以在 config_test.yml
中将服务覆盖为 public
(仅适用于 test
环境),如下所示:
services:
Psr\Log\LoggerInterface:
public: true
这通常用于测试私人服务。
在一个symfony 4项目中,很多services/controllers需要日志。 尝试利用 symfony 提供的特征和自动装配选项的优势,我创建了一个将传递给不同服务的 loggerTrait。
namespace App\Helper;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
trait LoggerTrait
{
/** @var LoggerInterface */
private $logger;
/** @var array */
private $context = [];
/**
* @return LoggerInterface
*/
public function getLogger(): LoggerInterface
{
return $this->logger;
}
/**
* @required
*
* @param LoggerInterface|null $logger
*/
public function setLogger(?LoggerInterface $logger): void
{
$this->logger = $logger;
}
public function logDebug(string $message, array $context = []): void
{
$this->log(LogLevel::DEBUG, $message, $context);
}
...
}
(灵感来自 symfonycasts.com)
服务将使用此特征
namespace App\Service;
use App\Helper\LoggerTrait;
class BaseService
{
use LoggerTrait;
/** @var string */
private $name;
public function __construct(string $serviceName)
{
$this->name = $serviceName;
}
public function logName()
{
$this->logInfo('Name of the service', ['name' => $this->name]);
}
}
它完美运行,但我无法成功测试它。
我试图在测试中扩展 KernelTestCase 以模拟 loggerInterface,但我收到 Symfony\Component\DependencyInjection\Exception\InvalidArgumentException:"Psr\Log\LoggerInterface" 服务是私有的,您无法替换它完美的感觉。
这是我的测试:
namespace App\Tests\Service;
use App\Service\BaseService;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class BaseServiceTest extends KernelTestCase
{
private function loggerMock()
{
return $this->createMock(LoggerInterface::class);
}
protected function setUp()
{
self::bootKernel();
}
/**
* @test
* @covers ::logName
*/
public function itShouldLogName()
{
// returns the real and unchanged service container
$container = self::$kernel->getContainer();
// gets the special container that allows fetching private services
$container = self::$container;
$loggerMock = $this->loggerMock();
$loggerMock->expect(self::once())
->method('log')
->with('info', 'Name of the service', ['name' => 'service_test']);
$this->logger = $container->set(LoggerInterface::class, $loggerMock);
$baseService = new BaseService('service_test');
var_dump($baseService->getLogger());
}
}
是否有在服务内部测试此类记录器的解决方案?
您可以在 config_test.yml
中将服务覆盖为 public
(仅适用于 test
环境),如下所示:
services:
Psr\Log\LoggerInterface:
public: true
这通常用于测试私人服务。