首页 > 解决方案 > 如何在 symfony 4 上使用 PHPUnit 测试依赖注入

问题描述

在 symfony 4 项目中,许多服务/控制器需要日志。尝试利用 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: The "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());
    }
}

是否有解决方案可以在服务内测试这样的记录器?

标签: symfonydependency-injectionphpunittraits

解决方案


您可以将服务覆盖为public(仅适用于test环境),config_test.yml如下所示:

services:
  Psr\Log\LoggerInterface:
    public: true

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


推荐阅读