Testing Email in Drupal with Behat

Adding the following to a FeatureContext.php will provide basic functionality to test for an email message sent to the current user and check for a partial subject match.  It changes the existing mail system to one that stores mail in Drupal's state allowing us to iterate through the messages sent without having to implement an email client.

  /**
  * @BeforeScenario @email
  */
  public function beforeEmailScenario(\Behat\Behat\Hook\Scope\BeforeScenarioScope $scope) {
    // get mail config
    $config = \Drupal::configFactory()->getEditable('system.mail');

    // save the current settings so we can restore them later
    $this->defaultEmailSettings = $config->get('interface.default');

    // use the test_mail_collector to store all messages in system.test_mail_collector state
    $config
      ->set('interface.default', 'test_mail_collector')
      ->save();

    // clear out any existing messages in system.test_mail_collector
    \Drupal::state()->set('system.test_mail_collector', []);
  }

  /**
   * @AfterScenario @email
   */
  public function afterEmailScenario(\Behat\Behat\Hook\Scope\AfterScenarioScope $scope) {

    // get mail config
    $config = \Drupal::configFactory()->getEditable('system.mail');

    // restore the original mail seetings
    $config
      ->set('interface.default', $this->defaultEmailSettings)
      ->save();
  }

  public function getEmailMessageSubject($message) {
    return $message['subject'];
  }

  public function getEmailMessageRecipient($message) {
    return $recipient = $message['to'];
  }

  /**
   * @Then an email should be sent to me with a subject containing :expectedSubject
   */
  public function anEmailShouldBeSentToMeWithASubjectContaining($expectedSubject) {

    // reset cache
    \Drupal::state()->resetCache();

    // get the messages
    $messages = \Drupal::state()->get('system.test_mail_collector');

    // check each message
    foreach ($messages as $message) {
      $email = $this->getUserManager()->getCurrentUser()->mail;

      // see if the messages was sent to us and the subject matches, we found a good message
      if ($expectedRecipient == $this->getEmailMessageRecipient($message) &&
          stripos($this->getEmailMessageSubject($message), $expectedSubject) >= 0) {
        return;
      }
    }
    // if we got this far, we didn't find the message we were looking for
    throw new Exception("Couldn't find message sent to me with subject '$expectedSubject'");
  }

Then in your scenario you can use the step:

Then an email should be sent to me with a subject containing "Account details for "