Reputation: 2686
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 [email protected] 1', Array ()) does not match expected value.
├ Failed asserting that 'Group mail sent to [email protected] 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?
Upvotes: 1
Views: 1633
Reputation: 35169
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.
Upvotes: 1
Reputation: 1048
The function you are looking for is withConsecutive()
instead of with()
. This allows you to pass along multiple arguments. Your assertion would look like this:
$mockLogger->expects($this->exactly(3))
->method('info')
->withConsecutive(['Owner mail sent to'],['Pno mail sent to'],['Group mail sent to']);
The arguments passed are in arrays because it is of course possible to call a function with multiple arguments in a single call. You could then pass in an array with multiple variables to test this.
Upvotes: 0