We've found ourselves needing some higher-level tests for some emails that were being sent recently (via a command that's run regularly from a cron) - here's how we did it!
We wanted to check that the email content and attachments were correct once we’d run the command.
We could have had the emails sending, and be caught by something like Mailcatcher - but that wasn’t a route we wanted to go down. We didn’t need to actually check that emails are fully sending via our email provider.
Instead, we configured Swift Mailer to spool emails to a file (these are ignored from git of course) in our test environment.
Then within our tests, we can check the file on disk for the correct contents and attachments. This happens within Behat contexts currently.
These tests aren’t particularly fast (so you don’t want many of them, just the critical path), and there’d be lower-level ways of doing this, which we’ll be refactoring to at some point.
This relies on the Behat Symfony2 Extension
app/config/config_test.yml
swiftmailer:
disable_delivery: true
spool:
type: file
path: '%kernel.root_dir%/spool'
delivery_addresses: ~
Then we have an EmailContext
that handles a @BeforeScenario
hook:
<?php
namespace Vivait\Context;
use Behat\Behat\Context\Context;
use Behat\Symfony2Extension\Context\KernelDictionary;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpFoundation\File\File;
class EmailContext implements Context
{
use KernelDictionary;
/**
* We need to purge the spool between each scenario
*
* @BeforeScenario @clear-emails
*/
public function purgeSpool()
{
$filesystem = new Filesystem();
$finder = $this->getSpooledEmails();
/** @var File $file */
foreach ($finder as $file) {
$filesystem->remove($file->getRealPath());
}
}
/**
* @return Finder
*/
public function getSpooledEmails()
{
$finder = new Finder();
$spoolDir = $this->getSpoolDir();
$finder->files()->in($spoolDir);
return $finder;
}
/**
* @param $file
*
* @return string
*/
public function getEmailContent($file)
{
return unserialize(file_get_contents($file));
}
/**
* @return string
*/
protected function getSpoolDir()
{
return $this->getContainer()->getParameter('swiftmailer.spool.default.file.path');
}
}
This has a @BeforeScenario
hook that looks for @clear-emails
on a scenario. If so, it’ll clear out the files from any previously sent emails in tests so we’re working with a clean directory.
Now if we need to check any emails within that directory, we can loop through them in contexts and check whatever we may need to:
$files = $this->emailContext->getSpooledEmails();
$found = false;
/** @var File $file */
foreach ($files as $file) {
/** @var Swift_Message $data */
$data = $this->emailContext->getEmailContent($file);
/*
* Check whatever we may need to check with $data
* You can check data, contents, subject, attachments, addresses etc
*/
$found = true;
}
Assert::assertNotFalse($found, 'No emails found.');
There’s lots of other ways to do this, but this is what we’ve settled on currently.
For information on how to access other contexts in Behat 3 (like you see here with $this->emailContext
) see this gist from stof or the page on the behat documentation
Article written by:
Matt
A BDD and TDD fan, his aims and goals are to mentor and help the developers to grow, and to continue making great software.
Recently while debugging some tests that were failing on our build server, but passing locally, we needed to see the state of the Database for the application when a test had failed - using Behat hooks and the Symfony Process component, here's how we did it.
Read more about Dumping your database on scenario failure in behatRecently I discovered the finer controls for whitespace within twig, beyond just the 'spaceless' option.
Read more about Controlling whitespace in TwigRecently when writing some tests using a data provider in PHPUnit, I wanted to be able to re-run a specific set, rather than the test with every single set in the data provider. I discovered the filtering options available in PHPUnit were a lot more powerful than I'd first thought.
Read more about Running PHPUnit with a specific data provider set