Testing Symfony2 emails with Behat 3
Asked Answered
F

2

5

I followed the Behat 2.5 docs to test mails. After a few tweaks to match Behat 3 I have ended with the following code (I have removed non-relevant parts):

public function getSymfonyProfile()
{
    $driver = $this->mink->getSession()->getDriver();

    if (!$driver instanceof KernelDriver) {
        // Throw exception
    }

    $profile = $driver->getClient()->getProfile();
    if (false === $profile) {
        // Throw exception
    }

    return $profile;
}

/**
 * @Then I should get an email with subject :subject on :email
 */
public function iShouldGetAnEmail($subject, $email)
{
    $profile   = $this->getSymfonyProfile();
    $collector = $profile->getCollector('swiftmailer');

    foreach ($collector->getMessages() as $message) {
        // Assert email
    }

    // Throw an error if something went wrong
}

When I run this test, it throws the following error:

exception 'LogicException' with message 'Missing default data in Symfony\Bundle\SwiftmailerBundle\DataCollector\MessageDataCollector' in vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/DataCollector/MessageDataCollector.php:93
Stack trace:
    #0 vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/DataCollector/MessageDataCollector.php(122): Symfony\Bundle\SwiftmailerBundle\DataCollector\MessageDataCollector->getMailerData('default')
    #1 features/bootstrap/FeatureContext.php(107): Symfony\Bundle\SwiftmailerBundle\DataCollector\MessageDataCollector->getMessages()

My profiler is configured as follows:

# app/config/config_test.yml
framework:
    test: ~
    profiler:
        enabled: true
        collect: true

It seems that the Profile is correctly loaded and the MessageDataCollector from Swiftmailer does exist, but it is not doing its work as expected. Any clue to solve this?

Follett answered 21/11, 2014 at 12:6 Comment(2)
Possibly #26270993Obediah
No, that's a different issue, I didn't touch AppKernel.phpFollett
C
4

Maybe the issue you have has been fixed as I do not have this anymore (I'm using Behat v3.0.15, BrowserKit driver 1.3.* and Symfony v2.6.6).

I managed to reproduce your error but only when I forgot to enable profiler data collecting:

profiler:
    collect: false

Once this problem solved (the configuration you provided solving the problem for me) I managed to check emails in my Behat tests. Two solutions for this:

Solution #1: Intercepting redirects globally

If it does not break all your other tests you can do so by configuring your web profiler as follows:

web_profiler:
    intercept_redirects: true

Solution #2: Preventing client to follow redirections temporarily

For my part, intercepting redirections globally in the configuration broke most of my other functional tests. I therefore use this method instead.

As preventing redirections allows mainly to check data in the data collectors I decided to use a tag @collect on each scenario requiring redirect interception. I then used @BeforeScenario and @AfterScenario to enable this behaviour only for those scenarios:

/**
 * Follow client redirection once
 *
 * @Then /^(?:|I )follow the redirection$/
 */
public function followRedirect()
{
    $this->getDriver()->getClient()->followRedirect();
}

/**
 * Restore the automatic following of redirections
 *
 * @param BeforeScenarioScope $scope
 *
 * @BeforeScenario @collect
 */
public static function disableFollowRedirects(BeforeScenarioScope $scope)
{
    $context = $scope->getEnvironment()->getContext(get_class());
    $context->getDriver()->getClient()->followRedirects(false);
}

/**
 * Restore the automatic following of redirections
 *
 * @param AfterScenarioScope $scope
 *
 * @AfterScenario @collect
 */
public static function restoreFollowRedirects(AfterScenarioScope $scope)
{
    $context = $scope->getEnvironment()->getContext(get_class());
    $context->getDriver()->getClient()->followRedirects(true);
}
Capetian answered 6/5, 2015 at 8:52 Comment(1)
Exactly, latest versions solve the exception I was getting but still I needed to stop following redirections to access the collected messages (redirections seem to clean the collector). In my case, instead of disabling redirections for the scenario, I defined a "Then I stop following redirections" step; but it is the same idea as yours. Thank you very much!Follett
J
4

It's not the answer your are looking for, but I'm pretty sure it will suits your needs (perhaps more).
If I can suggest, try using Mailcatcher with this bundle: https://packagist.org/packages/alexandresalome/mailcatcher
You'll be able to easily tests if emails are sent, what's their subject, follow a link in the body, and so on...
Many steps are included with this bundle.

Juggle answered 31/12, 2014 at 15:53 Comment(6)
This bundle is not yet compatible with behat 3Swaim
take a look at this blog elao.com/fr/blog/… maybe you find a solution.Eckardt
+1 for using mailcatcher. it will do end to end testing and you can be sure that the email sending is tested properly. using symfony profiler is more of a hack. if there is some edge case and a bug in swiftmailer or profiler, you might not catch it.Gelding
@Swaim This bundle is now compatible with behat 3 (since last year I think). github.com/alexandresalome/mailcatcher/pull/9Juggle
lol these commit messages are the worst! Anyway, have my upvoteSwaim
This is genius! <3 Works like a charm :-)Detradetract
C
4

Maybe the issue you have has been fixed as I do not have this anymore (I'm using Behat v3.0.15, BrowserKit driver 1.3.* and Symfony v2.6.6).

I managed to reproduce your error but only when I forgot to enable profiler data collecting:

profiler:
    collect: false

Once this problem solved (the configuration you provided solving the problem for me) I managed to check emails in my Behat tests. Two solutions for this:

Solution #1: Intercepting redirects globally

If it does not break all your other tests you can do so by configuring your web profiler as follows:

web_profiler:
    intercept_redirects: true

Solution #2: Preventing client to follow redirections temporarily

For my part, intercepting redirections globally in the configuration broke most of my other functional tests. I therefore use this method instead.

As preventing redirections allows mainly to check data in the data collectors I decided to use a tag @collect on each scenario requiring redirect interception. I then used @BeforeScenario and @AfterScenario to enable this behaviour only for those scenarios:

/**
 * Follow client redirection once
 *
 * @Then /^(?:|I )follow the redirection$/
 */
public function followRedirect()
{
    $this->getDriver()->getClient()->followRedirect();
}

/**
 * Restore the automatic following of redirections
 *
 * @param BeforeScenarioScope $scope
 *
 * @BeforeScenario @collect
 */
public static function disableFollowRedirects(BeforeScenarioScope $scope)
{
    $context = $scope->getEnvironment()->getContext(get_class());
    $context->getDriver()->getClient()->followRedirects(false);
}

/**
 * Restore the automatic following of redirections
 *
 * @param AfterScenarioScope $scope
 *
 * @AfterScenario @collect
 */
public static function restoreFollowRedirects(AfterScenarioScope $scope)
{
    $context = $scope->getEnvironment()->getContext(get_class());
    $context->getDriver()->getClient()->followRedirects(true);
}
Capetian answered 6/5, 2015 at 8:52 Comment(1)
Exactly, latest versions solve the exception I was getting but still I needed to stop following redirections to access the collected messages (redirections seem to clean the collector). In my case, instead of disabling redirections for the scenario, I defined a "Then I stop following redirections" step; but it is the same idea as yours. Thank you very much!Follett

© 2022 - 2024 — McMap. All rights reserved.