Dr Nic

Testing outbound emails with Cucumber

My testimonial for Cucumber still stands even in 2009. In fact I promise to let you know when I don’t think Cucumber is the bees-knees of integration testing. I love the step-by-step English instructions of user usage scenarios backed by a simple Ruby DSL for describing real actions on your application for each step.

For testing Rails apps, Cucumber defaults to using Webrat on top of Rails’ own integration sessions. With Webrat you actually test that your views match to your controller actions. If I click the “Submit” button, then it checks that it invokes an available action correctly. Before Cucumber I could appreciate the merit of Webrat alone, but the two tools combined are instant superheroes of my Rails development crime fighting team. That is, like any superhero TV show at least once every 30 minutes you find yourself saying “Thank you Mr Cucumber and your trusty sidekick Webrat, you’re my heroes”. (BTW, I’m not alone in this analogy)

Testing emails

But I wanted to test emails. More interestingly, emails containing links back into my application. Like an activation email on sign up.

Specifically, I wanted a cucumber scenario like this:

Scenario: Signup for new account
  Given I am on the signup form
  When I fill in "Email" with "drnic@mocra.com"
  And I press "Join"
  Then I should see "An activation email has been sent"
  And I should receive an activation email
  When I click the activation email link
  Then I should see "Your account is active"

Within this scenario there are 7 steps. Lines 2, 3, 4 and 7 match to steps from the generated webrat_steps.rb file when you install cucumber.

Line 1 also matches to a webrat step definition. But it requires that you define what “the signup form” maps to in your routes. So you need to update features/support/paths.rb to specify what “the signup form” url is:

module NavigationHelpers
  def path_to(page_name)
    case page_name
    when /the signup form/
      signup_path
    end
  end
end

So, that leaves us with lines 5 and 6.

email-spec plugin

I’m happy using the email-spec gem/plugin from Ben Mabey, who is also the lead maintainer of the awesome Cucumber TextMate bundle and did a wonderful presentation on Outside-In Development with Cucumber at the recent Mountain West RubyConf (lots of great videos available)

To install as a plugin:

script/plugin install git://github.com/bmabey/email-spec.git

Then add the following line to your *feature/support/env.rb* file:

require 'email_spec/cucumber'

Finally, the plugin comes with some bonus cucumber step definitions which wrap around lots of nice helpers:

script/generate email_spec

Using email-spec step definitions

Let’s ignore my desired lines 5 and 6 above and use the step definitions that we get with email-spec. We can replace the two lines with the following:

And I should receive an email
When I open the email
Then I should see "Please activate your new account" in the subject
When I click the first link in the email

Update: The last line didn’t used to be in email-spec but it now is. I’ve removed the example from this article.

I did all this for an existing application and every line of the scenario tested positive/green. Yay!

Refactoring four steps into two

For the sake of demonstration, you now might want to refactor these four steps into two steps to keep your scenarios nice and readable.

That is, how can we convert these 4 steps into our original lines 5 and 6?

The quickest way is to copy and paste the lines and slap some quotes around the text:

Then /^I should receive an activation email$/ do |email|
  Then 'I should receive an email'
  When 'I open the email'
  Then 'I should see "Please activate your new account" in the subject'
end

When /^I click the activation email link$/ do
  When 'I click the first link in the email'
end

That’s right, you can use Given/When/Then as invocation methods as well as step definition methods. If you don’t pass these methods a do…end block then they will match/find/invoke a step rather than define a new one. Very cool.

Alternately, you could implement the two steps using the underlying email helper methods provided by the email-spec plugin:

Then /^I should receive an activation email$/ do
  unread_emails_for(current_email_address).size.should == 1
  open_email(current_email_address)
  current_email.should have_subject(/Please activate your new account/))
end

When /^I click the activation email link$/ do
  click_first_link_in_email
end

Your choice.

Configuring current_email_address

All the email-spec helper methods assume that the “I” in the scenario has an email address. It uses a method current_email_address for this.

You must change the current_email_address method in the email_steps.rb file to pull out email addresses from wherever they might be located within any given scenario.

For example, here is one definition of the method from one of my projects:

def current_email_address
  @email || (@current_user && @current_user.email) || "drnic@mocra.com"
end

Summary

With Cucumber and email-spec it is very easy to send and test emails. So my thanks go to Ben Mabey for this plugin.

Related posts:

  1. Dead simple JavaScript Unit Testing in Rails Formats: Video/Screencast (410 Mb, torrent) | Video only (vimeo)...
  2. During integration tests you might want to load public/ files I’ve been wondering recently “If I cache a file in...
  3. hash bang cucumber I don’t know if this is a good idea or...
  4. Cucumber: building a better World (object) How to write helper libraries for your Cucumber step definitions...
  5. Closing in on The Dream: “one-click-to-deploy Rails apps” Got a simple app you want to build? Allocate...

11 Responses to “Testing outbound emails with Cucumber”

  1. Paco Guzmán says:

    Very cool!!

    Now I’m going to view the Ben’s presentations at the recent Mountain West RubyConf.

  2. [...] Testing outbound emails with Cucumber – Dr Nic My testimonial for Cucumber still stands even in 2009. In fact I promise to let you know when I don’t think Cucumber is the bees-knees of integration testing. I love the step-by-step English instructions of user usage scenarios backed by a simple Ruby DSL for describing real actions on your application for each step. (tags: cucumber rubyonrails rspec email) [...]

  3. [...] Testing outbound emails with Cucumber – Dr Nic is still high on Cucumber. I’m not especially, but this is a useful walkthrough if you’re getting into BDD. [...]

  4. [...] Nic still loves Cucumber too. Really like this article on the nitty-grtty details of testing outbound emails using [...]

  5. [...] Dr Nic’s Testing outbound emails with Cucumber. [...]

  6. [...] amplias y os aconsejo que le echéis un ojo al archivo eatures/step_definitions/email_steps.rb y a este post en inglés que realiza un buen análisis incluyendo ejemplos. metapost, rubyonrails, testing / cucumber, email, ror, rubyonrails, [...]

  7. [...] This post was Twitted by jbpros [...]

  8. Rajan says:

    Thanks Dr. Nic for the email with cucumber article.
    We’ve been trying to implement this and we are finding following error.

    And I should receive an activation email # features/step_definitions/email_steps.rb:26
    Your block takes 1 argument, but the Regexp matched 0 arguments. (Cucumber::ArityMismatchError)
    features/step_definitions/email_steps.rb:26:in `/^I should receive an activation email$/’
    features/register.feature:15:in `And I should receive an activation email’

    Any ideas will help.

    Regards
    Rajan

  9. [...] application: views, controllers, models and other utility classes (such as email handling, see this great post on email_spec) can all be tested in a much more natural [...]

  10. Andy Waite says:

    @rajan – just remove |email| from the following line, I think it’s a mistake:

    Then /^I should receive an activation email$/ do |email|