Tweetegy On the edge of chaos with Ruby, Rails, JavaScript and AngularJS.

| About | Search | Archive | Github | RSS |

Testing Rails ActionMailer using RSpec Shared Examples

I love ActionMailer in Rails, for many reasons, but one of them is that they are easy to test! I work with mailers a lot in many projects and I do find that I often follow similar testing patterns for each mailer in each project. In this post, I’ll demonstrate using RSpec shared examples to reuse some of the mailer specs across multiple mailers.

An ActionMailer

What does it take to create an ActionMailer in Rails 4? A generator, of course!

1 rails g mailer CoolMailer email

What you should end up with is a basic Mailer class that inherits from ActionMailer::Base and related views (+ specs, of course!).

Testing these puppies!

What we need to do now is actually test these puppies. As we know there are a large number of similarities between most mailers. For example, the from property needs to be set to something appropriate for your application or context. The to property also needs to be set, of course! We usually want the email to contain a body and we usually want to check that certain content is rendered (or not) within it. Sometimes we want to send multipart emails for supporting both text and html clients.

Other things that may be generic mailer tests:

The way I test most of this is using a shared example similar to the one below.

 1 shared_examples "a well tested mailer" do
 2   let(:greeting) { "Darren" }
 3   let(:full_subject) { "#{asserted_subject}:You got mail!" }
 4   let(:mail) { }
 6   it "renders the headers" do
 7     mail.content_type.should start_with('multipart/alternative') #html / text support
 8   end
10   it "sets the correct subject" do
11     mail.subject.should eq(full_subject)
12   end
14   it "includes asserted_body in the body of the email" do
15     asserted_body.each do |content|
16       mail.body.encoded.should match(content)
17     end
18   end
20   it "should be from ''" do
21     mail.from.should include('')
22   end
23 end

Somethings I want to point out about this shared example:

Notice that we can treat this shared example, as all shared examples can, like a method call where we ‘pass in’ certain parameters to work against. You may notice in the above shared examples, variables listed that are not explicitly declared like full_subject, asserted_body and mailer_class. These are set outside of the shared example in the calling spec as shown below:

 1 describe CoolMailer do
 2   describe "email" do
 3     let(:mailer_class) { CoolMailer }
 4     let(:asserted_subject) { "A cool mail!" }
 5     let(:asserted_body) { ["This content", "That content"] }
 7     it_behaves_like "a well tested mailer" do
 8     end
 9   end
10 end

A word on multipart html/text emails

You will notice in the shared example a spec for the headers to start with ‘multipart/alternative’. This ensures that our emails are being sent out in both plain text and html. How do we actually get this spec to pass? Fortunately this just works out of the box with ActionMailer! All we need to do is provide 2 templates; one for html and one for plain text as follows:


The main thing that gets this to work is good old Rails ‘convention over configuration’. By simply placing the ‘.text.erb’ template and ‘.html.erb’ template with the same name (‘email’) ActionMailer automaticlly sends out a multipart email! Its that easy! Try removing the ‘.text.erb’ template and you will see the header spec fail with:

expected "text/html; charset=UTF-8" to start with "multipart/alternative"


The purpose of this post was to demonstrate two things: testing mailers and using shared examples. You don’t have to use shared examples to test mailers, especially if you only have one mailer in your project. However, most large Rails applications will have multiple mailers so using shared examples makes sense.

As always, an example application is available for download here: Testing Mailers example application