I have a service class in my symfony5 project on php8, which is sending emails following certain rules. With my test want to check if the correct mails are sent. This task exists for several projects, so I really want to find a solution for this.
The method, which collects the receiver of the mails is currently private and you should not expose your privates.
One idea was to write a log entry for each potential mail receiver. But how can I check for certain values in my test.log file?
I found a package, which extends phpunit just for this purpose ( https://github.com/phoenixrvd/phpunit-assert-log-entry ), but it doesn't look like it is maintained.
Do you know a solution for either
Currently I use a Trait to add logging functionality to my service, which has a setter method, which sets the Logger via the @required
annotation.
namespace App\Logger;
use Psr\Log\LoggerInterface;
trait LoggerTrait
{
private $logger;
/**
* @required
*/
public function setLogger(LoggerInterface $logger): void
{
$this->logger = $logger;
}
protected function logInfo(string $message, array $context = [])
{
if (null !== $this->logger) {
$this->logger->info($message, $context);
}
}
One of the big advantages of dependency injection - passing dependencies in, rather than creating them on demand - is that you can replace them with test doubles - Stubs, Mocks, and Spies. PHPUnit supports test doubles out of the box with everything you need.
To assert that a particular line is logged, you need to:
LoggerInterface
info
methodFor example:
public function testLogListsEmailAddress() {
// Create the mock
$mockLogger = $this->createMock(LoggerInterface::class);
// Expect a call to info, with the e-mail address in the message
$mockLogger->expects('info')
->with($this->stringContains('user@example.com');
// Set up the class to test
$classUnderTest = new MyThingummy;
$classUnderTest->setLogger($mockLogger);
// Run the code which should log the address
$classUnderTest->doSomething('user', 'example.com');
}
Asserting that a particular e-mail is sent would be done similarly: the class that actually sends the e-mails should be a dependency of the class that decides which e-mails to send. So you might have something like this:
public function testLogSendsEmail() {
$mockEmailSender = $this->createMock(EmailSenderInterface::class);
$mockEmailSender->expects('send')
// first argument to send() must be expected address; others can be anything
->with('user@example.com', $this->anything());
// Perhaps in this case, the dependency is passed to the constructor
$classUnderTest = new MyThingummy($mockEmailSender);
$classUnderTest->doSomething('user', 'example.com');
}