Email delivery in test environments

ActionMailer is a really nice framework for sending email. In particular, the ability to make the mailer store messages in an array for testing is a great feature. One thing that isn’t well handled is what do you do with staging environments that use a copy of production data? While we’re testing, we want to see the messages that are sent, but we don’t want them to go to our real users. I’ll show you a simple method for handling this case.

In a staging environment, I don’t want email to go to the users from the database. Instead, I want all emails to go to a single account. This allows me to view the email and click the links without worrying about email messages from the test system polluting our inboxes. The only question is how do we acheive this?

Luckily, ActionMailer itself is pretty easy code to read. By tracing the path of the delivery methods, I came to a point where it calls perform_delivery_#{delivery_method}. How nice is that? The configuration directive config.action_mailer.delivery_method = :test means that perform_delivery_test will be called whenever an email is going to be sent in that environment. That means all we need to do is to create our own perform delivery method and set that configuration option. A few minutes later, we had:

module ActionMailer
  class Base
  def perform_delivery_staging(mail)
      mail.to="staging@elevatedrails.com"
      mail.cc=""
      mail.bcc=""
      perform_delivery_sendmail(mail)
    end
  end
end

Now, if you set config.action_mailer.delivery_method = :staging in an environment, all messages will be sent to staging@elevatedrails.com. What a simple solution!

Update on Rails 3

This method no longer works on Rails 3. Instead, you need to use a slightly different method. Instead, you can use an intercepter as shown in this Railscast.