如何在 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

这通常用于测试私人服务。