Override ActionMailer recipient in Rails 2 and 3 for development or testing

If your like me, you might want enable delivering of mail in development, mostly because testing email layout is a pain without actually sending the email to a real client.

In doing this you’ll probably want to avoid the possibility of emails making their way to your customers from your development machine, this can be achieved with a simple initializer to override the destination.

Rails 2.x

1
2
3
4
5
6
7
8
9
10
11
12
if Rails.env.development?
  class ActionMailer::Base
    def create_mail_with_overriding_recipients
      mail = create_mail_without_overriding_recipients
      mail.to = "mail@robaldred.co.uk"
      #mail.cc =
      #mail.bcc =
      mail
    end
    alias_method_chain :create_mail, :overriding_recipients
  end
end

Rails 3.x

1
2
3
4
5
6
7
8
if Rails.env.development?
  class OverrideMailReciptient
    def self.delivering_email(mail)
      mail.to = "mail@robaldred.co.uk"
    end
  end
  ActionMailer::Base.register_interceptor(OverrideMailReciptient)
end

If you prefer to use a plugin, there is one called mail_safe personally, i think it’s a little overkill.

Testing ActionMailer models with Rspec

You might think testing ActionMailer sounds like a challenge, actually it’s just as easy as testing other classes, you just have to know the methods to use to change the state/content of the deliveries.

Here we have a simple example of how you might test a mailer for your order receipt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
require File.dirname(__FILE__) + '/../spec_helper'

describe OrderMailer do
 
  describe "receipt" do
 
    before(:each) do
      @order = mock_model(Order)
      @order.customer = mock_model(Customer)
    end
 
    it "should render successfully" do
      lambda { OrderMailer.create_receipt(@order) }.should_not raise_error
    end
 
    describe "rendered without error" do
   
      before(:each) do
        @mailer = OrderMailer.create_receipt(@order)
      end
   
      it "should have an order number" do
        @mailer.body.should have_tag('.order_number', :text => @order.id)
      end
   
      it "should have order details" do
        @mailer.body.should have_tag(".order_details")
      end
 
      it "should have a billing address" do
        @mailer.body.should have_tag(".billing.address")
      end
   
      it "should have a delivery address" do
        @mailer.body.should have_tag(".delivery.address")
      end
 
      it "should have customer contact details" do
        @mailer.body.should have_tag("#contact_details") do
          with_tag('.landline', :text => @order.customer.landline)
          with_tag('.mobile', :text => @order.customer.mobile)
          with_tag('.email', :text => @order.customer.email)
        end
      end
 
      it "should have a list of items" do
        @mailer.body.should have_tag(".line_items")
      end
 
      it "should have totals" do
        @mailer.body.should have_tag(".totals") do
          with_tag('.sub_total', :text => format_price(@order.sub_total))
          with_tag('.vat', :text => format_price(@order.vat))
          with_tag('.postage', :text => format_price(@order.postage))
          with_tag('.total', :text => format_price(@order.total))
        end
      end
     
      it "should deliver successfully" do
        lambda { OrderMailer.deliver(@mailer) }.should_not raise_error
      end
     
      describe "and delivered" do
       
        it "should be added to the delivery queue" do
          lambda { OrderMailer.deliver(@mailer) }.should change(ActionMailer::Base.deliveries,:size).by(1)
        end
       
      end
   
    end

  end
 
end

rendering the following output:

ActionMailer test output from textmate rspec bundle
ActionMailer test output from textmate rspec bundle