Tester l’envoi d’emails avec behat pour Drupal

Behat ne permet pas à priori de tester l’envoi d’email, ni de vérifier leur contenu. Mais, il est possible de rajouter cette fonctionnalité.

1ère étape on va rajouter un système de collecte des emails dans une variable qu’on pourra ensuite interroger. Il y a peu de nettoyage à faire car seul le title, send-to, send-from, et body nous intéressent vraiment :

<?php 

/** 
 * A mail sending implementation that captures sent messages to a variable. 
 * 
 * This class is for running tests or for development. Inspired from TestingMailSystem
 */ 
class EMHMailSystem extends DefaultMailSystem implements MailSystemInterface { 

  /** 
   * Save an e-mail message to a file, using Drupal variables and default settings. 
   * 
   * @see http://php.net/manual/en/function.mail.php * @see drupal_mail() 
   * 
   * @param $message * A message array, as described in hook_mail_alter(). 
   * @return 
   * TRUE if the mail was successfully accepted, otherwise FALSE. 
   */ 
  public function mail(array $message) { 
    $output = $this->composeMessage($message);
    unset($message['params']['context']['state']);
    unset($message['params']['context']['action']);
    $captured_emails = variable_get('drupal_test_email_collector', array());
    $captured_emails[] = $message;
    variable_set('drupal_test_email_collector', $captured_emails);
    return TRUE;
  }

}

2ème étape on rajoute le support pour behat. Attention, la recherche de pattern dans les emails (afin qu’on ne remonte pas une chaine trouvée dans un email qui n’a rien à voir) se fait via l’utilisation d’un email actif qui permet de chercher dans un email qu’on identifié au préalable, et aussi par la recherche dans le dernier email envoyé. Enfin il est possible de vérifier qu’un email n’existe pas.

  /**
   * @Given /^the test email system is enabled$/
   */
  public function theTestEmailSystemIsEnabled() {
    // Store the original system to restore after the scenario.
    $this->originalMailSystem = variable_get('mail_system', array('default-system' => 'DefaultMailSystem'));
    // Set the test system.
    variable_set('mail_system', array('default-system' => 'EMHMailSystem'));
    // Flush the email buffer, allowing us to reuse this step definition to
    // clear existing mail.
    variable_set('drupal_test_email_collector', array());
    // Delete queue from other test, can be overloaded if All Experts used.
    db_query("DELETE FROM queue WHERE name='emh_request_request_email_notification'");
    db_query('TRUNCATE TABLE {mail_logger}');
  }

  /**
   * @Then /^the email to "([^"]*)" should contain "([^"]*)"$/
   */
  public function theEmailToShouldContain($to, $contents) {
    // We cannot use variable_get() because $conf is only fetched once per
    // scenario.
    $variables = array_map('unserialize', db_query("SELECT name, value FROM {variable} WHERE name = 'drupal_test_email_collector'")->fetchAllKeyed());
    $this->activeEmail = FALSE;
    foreach ($variables['drupal_test_email_collector'] as $message) {
      if ($message['to'] == $to) {
        $this->activeEmail = $message;
        if (strpos($message['body'], $contents) !== FALSE ||
          strpos($message['subject'], $contents) !== FALSE) {
          return TRUE;
        }
        throw new \Exception('Did not find expected content in message body or subject.');
      }
    }
    throw new \Exception(sprintf('Did not find expected message to %s', $to));
  }

  /**
   * @Then /^the last email to "([^"]*)" should contain "([^"]*)"$/
   */
  public function theLastEmailToShouldContain($to, $contents) {
    $variables = array_map('unserialize', db_query("SELECT name, value FROM {variable} WHERE name = 'drupal_test_email_collector'")->fetchAllKeyed());
    $this->activeEmail = FALSE;
    foreach (array_reverse($variables['drupal_test_email_collector']) as $message) {
      if ($message['to'] == $to) {
        $this->activeEmail = $message;
        if (strpos($message['body'], $contents) !== FALSE ||
          strpos($message['subject'], $contents) !== FALSE) {
          return TRUE;
        }
        throw new \Exception('Did not find expected content in message body or subject.');
      }
    }
    throw new \Exception(sprintf('Did not find expected message to %s', $to));
  }

  /**
   * @Then /^the last email to "([^"]*)" should not contain "([^"]*)"$/
   */
  public function theLastEmailToShouldNotContain($to, $contents) {
    $variables = array_map('unserialize', db_query("SELECT name, value FROM {variable} WHERE name = 'drupal_test_email_collector'")->fetchAllKeyed());
    $this->activeEmail = FALSE;
    foreach (array_reverse($variables['drupal_test_email_collector']) as $message) {
      if ($message['to'] == $to) {
        $this->activeEmail = $message;
        if (strpos($message['body'], $contents) == FALSE ||
          strpos($message['subject'], $contents) == FALSE) {
          return TRUE;
        }
        throw new \Exception('Found expected content in message body or subject.');
      }
    }
    // Dont care if not found any email at all.
  }

  /**
   * @Then /^there should be no email to "([^"]*)" containing "([^"]*)"$/
   */
  public function thereIsNoEmailToContaining($to, $contents) {
    $recipient = FALSE;
    $not_contains = FALSE;
    $variables = array_map('unserialize', db_query("SELECT name, value FROM {variable} WHERE name = 'drupal_test_email_collector'")->fetchAllKeyed());
    foreach ($variables['drupal_test_email_collector'] as $message) {
      if ($message['to'] == $to) {
        $recipient = TRUE;
        if (strpos($message['body'], $contents) == FALSE && strpos($message['subject'], $contents) == FALSE) {
          $not_contains = TRUE;
        }
      }
    }
    if (($recipient == TRUE && $not_contains == TRUE) || $recipient == FALSE) {
      return TRUE;
    }
    else {
      throw new \Exception('Found email and expected content in message body or subject.');
    }
  }

  /**
   * @Given /^the email should contain "([^"]*)"$/
   */
  public function theEmailShouldContain($contents) {
    if (!$this->activeEmail) {
      throw new \Exception('No active email');
    }
    $message = $this->activeEmail;
    if (strpos($message['body'], $contents) !== FALSE ||
      strpos($message['subject'], $contents) !== FALSE) {
      return TRUE;
    }
    throw new \Exception('Did not find expected content in message body or subject.');
  }

On peut enfin tester nos emails:

@api @watchdog
Feature: Contact
  In order to test the contact mail
  As an user
  I want to send a mail to contact 

  @email @nodelay
  Scenario: Test if the contact mail was sent
    Given the test email system is enabled
    When I visit '/contact'
      And I fill in "Bruce" for "firstname"
      And I fill in "Wayne" for "lastname"
      And I fill in "emh.test+batman@gmail.com" for "mail"
      And I fill in "Gotham City" for "message"
      And I press "Send"
    Then I should see the text "Your message has been sent."

    Then the last email to "contact@emh.com" should contain "(emh.test+batman@gmail.com)"
      And the email should contain "From - Bruce Wayne"

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *