http://drnicwilliams.com/2009/03/26/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