phpmockingphpunitsymfony5

PhpUnit check for more than one log message


In my project I have a class implementing the facade pattern meaning I have one method, which in the background is calling loads of classes and doing "things". One of the is writing several log messages after having sent emails. So when the log message is written I know that the email was sent. Also the classes which are called write some log messages.

So my task is to check in a bunch of triggered log messages if at least one matches my expectations.

I know how to mock my logger, so I can check for the existence of one single log message. But what I need in my case is to check if several log messages are written. I successful I know that my facade method call triggered all the correct classes/methods.

This is what I have now and it clearly doesn't work:

...

        $mockLogger = $this->createMock(LoggerInterface::class);
        $mockLogger->expects($this->any())
                   ->method('info')
                   ->with($this->stringContains('Owner mail sent to'));
        $mockLogger->expects($this->any())
                   ->method('info')
                   ->with($this->stringContains('Pno mail sent to'));
        $mockLogger->expects($this->any())
                   ->method('info')
                   ->with($this->stringContains('Group mail sent to'));
        $this->facade->setLogger($mockLogger);
        $this->facade->sendEMails($reportDto);

this is the error I get:

Testing App\Tests\Unit\Domain\Mail\MailingFacadeTest
Mailing Facade (App\Tests\Unit\Domain\Mail\MailingFacade)
 ✘ Send e mails
   ┐
   ├ Expectation failed for method name is "info" when invoked 1 time(s)
   ├ Parameter 0 for invocation Psr\Log\LoggerInterface::info('Group mail sent to gf.test@ex...port 1', Array ()) does not match expected value.
   ├ Failed asserting that 'Group mail sent to gf.test@example.com as p221903 for Report 1' contains "pno mail sent to ".
   │
   ╵ /var/www/src/Logger/LoggerTrait.php:72
   ╵ /var/www/src/Domain/Mail/MailingFacade.php:59
   ╵ /var/www/src/Domain/Mail/MailingFacade.php:34
   ╵ /var/www/tests/Unit/Domain/Mail/MailingFacadeTest.php:63
   ┴

Is there a way to check if a log message with a certain string is at least once written to the log?


Solution

  • Rather than trying to setup 'expections' from a mock, I find it a lot easier to inject a logger (that implements the Psr\Log\LoggerInterface) to collect all the messages sent to the log, and then check after the call. Monolog has a 'TestHandler' for example that could could then be something like:

    <?php
    $log = new \Monolog\Logger('test');
    $testLog = new \Monolog\Handler\TestHandler();
    $log->pushHandler($testLog);
    
    $this->facade = new className($log);
    $this->facade->sendEMails(new DtoObject('x', 'y'));
    
    $this->assertTrue(
        $testLog->hasInfoThatContains('Owner mail sent to')
        || $testLog->hasInfoThatContains('Pno mail sent to')
        || $testLog->hasInfoThatContains('Group mail sent to'),
        'None of the possible messages were found'
    );
    

    I have a Trait I can use in PHPUnit tests that creates $this->log & $this->testLog so they can be used and read from.