如何在 symfony phpunit kernelTestCase 中正确注入 monolog.logger / LoggerInterface 作为模拟

Posted

技术标签:

【中文标题】如何在 symfony phpunit kernelTestCase 中正确注入 monolog.logger / LoggerInterface 作为模拟【英文标题】:How to properly inject monolog.logger / LoggerInterface as mock in a symfony phpunit kernelTestCase 【发布时间】:2021-10-08 17:00:40 【问题描述】:

我想在服务MyService 中测试服务调用是否是正确的方法$logger->info()$logger->error()

为此,我编写了以下测试:

use App\Service\MyService;
use App\Tests\Mock\MyServiceHttpClient;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

class MyServiceTest extends KernelTestCase

    public function testMyServiceShouldSucceed()
    
        self::bootKernel();
        $router = self::$kernel->getContainer()->get('router');
        $logger = $this->createMock(LoggerInterface::class);
        $logger->expects($this->never())->method('error');
        $logger->expects($this->once())->method('info');
        $myService = new MyService($logger, $router);
        $myService->someMethodIExpectToSucceed();
    

我的服务.php

namespace App\Service\MyService;

class MyService

    private LoggerInterface $myserviceLogger;
    private UrlGeneratorInterface $router;

    public function __construct(LoggerInterface $myserviceLogger, UrlGeneratorInterface $router) 
        $this->logger = $myserviceLogger;
        $this->router = $router;

    public function someMethodIExpectToSucceed(): void
    
        try 
            // … some code, api calls, …
            $this->logger->info("all was ok, I want that log");
         catch (\Exception $e) 
            $this->logger->error("something wrong happens");
        
    

这很好用,但我的服务有更多的依赖项,而不仅仅是 $logger$router。所以我尝试找到一种方法,首先将模拟注入内核容器中,如下所示:

        self::$kernel->getContainer()->set('myservice.logger', $logger);
        $myService = self::$kernel->getContainer()->get(MyService::class)->someMethodIExpectToSucceed();

但这似乎不起作用。我不能使用monolog.logger 代替myservice.logger,因为该服务是私有的,我认为在services_test.yaml 中重新声明monolog.logger 不是一个好主意。但也许我应该?

你有解决办法吗?

【问题讨论】:

【参考方案1】:

是的,如果您想在测试中动态重新定义服务,您应该将其定义为“公共”。这是 services_test.yaml 的主要用途。

【讨论】:

【参考方案2】:

我有一个 trait,当我想检查正在记录的内容时,在单元测试中使用它。

有了这个,我可以创建/__construct()我需要的所有其他模拟或真实服务,并创建我想要测试的内容,包括$this->log代替任何LoggerInterface,然后获取日志记录关于它的各种断言。

如果我需要在我自己从容器中获取的服务中定期使用它,我会制作类似于服务的东西(仅在测试环境中可用),并将其连接为默认记录器 - 或用于特定服务- 通过 services_test.yaml 文件(或在 packages/test/ 中),获取TestLog 服务并根据需要对其记录的内容进行检查。

<?php
use Monolog\Handler\TestHandler;
use Monolog\Logger;

/**
 * Create a dummy logger that can be inspected.
 *
 * Use `$this->log` on a LoggerInterface parameter to send logs to.
 * Make assertions on the contents of `$this->testLog`.
 *
 * Examples:
 * <code>
 *  * $this->testLog->reset();      // clear the previously log messages
 *  * $this->assertCount(1, $this->testLog->getRecords(), 'Only expected 1 log     message');
 *  * $this->assertTrue($this->testLog->hasDebugThatMatches('/from the API/'));
 * </code>
 */
trait TestLog

    protected Logger $log;
    protected TestHandler $testLog;

    /**
     * @before
     */
    public function createTestLog(): void
    
        $this->log = new Logger('test');
        $this->testLog = new TestHandler();
        $this->log->pushHandler($this->testLog);
    
    // more helper/assertion methods

【讨论】:

以上是关于如何在 symfony phpunit kernelTestCase 中正确注入 monolog.logger / LoggerInterface 作为模拟的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 PHPUnit 测试 Symfony 中的字符引用?

Symfony/PhpUnit - 如何测试服务中是不是抛出异常

如何在 symfony phpunit kernelTestCase 中正确注入 monolog.logger / LoggerInterface 作为模拟

Symfony 3模拟服务功能测试phpunit和liip

PHPUnit、接口和命名空间 (Symfony2)

Symfony 5 Phpunit 未定义方法