Rails: Developing Email-related features with rspec, email-spec and letter_opener

After years of being a developer, (.NET and Grails, now I’m 3 months into Ruby-land), I always run into productivity problems when developing email-related features. Here’s why:

  1. Write/Fix the code
  2. Send the email to an inbox
  3. Open the inbox
  4. Find something wrong
  5. Repeat 1-4 until satisfied

Sounds easy, but it’s really tedious. Your inbox fills up with test emails. Your ‘flow’ gets broken. So recently, I’ve marked a new milestone in my development career: that doesn’t happen anymore. Instead, this happens:

  1. Write a test, expecting what I want to appear in an email.
  2. Write the code
  3. Run the test, find something wrong
  4. (Optional) Run the mailer in the rails console, email pops up as an HTML file in my browser
  5. Repeat 1-4 until satisfied.

No more inbox related stuff, and I can cycle quickly through the usual development loop.

rspec, email_spec and letter_opener are the keys to how I became productive. Combining these gems are one of the many ways to get productive in churning out email-related code, but so far this is my most favorite.

rspec/email-spec

rspec needs no introduction – simply put, it’s a unit testing framework for ruby.

I don’t write unit tests for all of my code, since I know I’ll go crazy when I do and it’ll be counterproductive. I only write tests for my code when:

  1. It’s a piece of the system that’s really important and prone to errors.
  2. It’s a pain to replicate manually.

To cut it short, Unit tests helps me be confident that whatever I’m expecting to send in an email, is actually written there in that email.

email_spec are helpers for rspec to test emails. Taken right out of the README:

describe "Signup Email" do
  include EmailSpec::Helpers
  include EmailSpec::Matchers
  # include ActionController::UrlWriter - old rails
  include Rails.application.routes.url_helpers

  before(:all) do
     @email = UserMailer.create_signup("jojo@yahoo.com", "Jojo Binks")
  end

  it "should be set to be delivered to the email passed in" do
    @email.should deliver_to("jojo@yahoo.com")
  end

  it "should contain the user's message in the mail body" do
    @email.should have_body_text(/Jojo Binks/)
  end

  it "should contain a link to the confirmation link" do
    @email.should have_body_text(/#{confirm_account_url}/)
  end

  it "should have the correct subject" do
    @email.should have_subject(/Account confirmation/)
  end

end

letter_opener

letter_opener sends the email in the browser as an HTML file instead of really sending it. This means you do not need to set up email delivery in your development environment. Helpful for styling emails, and for integration tests (Cukes)

Taken right out of the README:

First add the gem to your development environment and run the bundle command to install it.

gem "letter_opener", :group => :development

Then set the delivery method in config/environments/development.rb

config.action_mailer.delivery_method = :letter_opener

Now any email will pop up in your browser instead of being sent. The messages are stored in tmp/letter_opener

TL;DR

rspec + letter_opener = developing any email-related feature is no longer painful.