メールを含むテストもEmail Spec で楽々

Webシステムではメールをユーザー送り、メール内のURLクリックで処理を行うようなケースも間々あります。そんなプログラムの受け入れテスト、End-to-Endテストを行うには Request Specs や Steak に Email Spec で簡単に書けます。

http://www.pragprog.com/images/covers/190x228/achbd.jpg

Email SpecRails の ActionMailer のメール送信部分をフックし送信されたメール内容をライブラリー内に保持し、RSpecでテストを書けるようにするライブラリーです。 RSpecベースなので Request Specs や Steakだけでなく Cucumber でも使えます。

インストール

  • Gemfile に gem 'email_spec' を追加し bundle install を実行。
  • spec_helper.rb に以下を追加
require 'action_mailer'        
require 'email_spec'
RSpec.configure do |config|
  config.include(EmailSpec::Helpers)
  config.include(EmailSpec::Matchers)
end

テスト対象のコード

Steakの記事 で書いたTodoのサンプルにTodoの更新したら、メールを送り、そのメール内のURLをクリックするとTodo一覧が表示されるという、いかにもサンプルらしいコードを作ります。

  • app/controller/todos_controller.rb 更新部分
  def update
    @todo = Todo.find(params[:id])

    respond_to do |format|
      if @todo.update_attributes(params[:todo])
        format.html { redirect_to(@todo, :notice => 'Todo was successfully updated.') }
        format.xml  { head :ok }
        mail = TodoMail.hello(todos_url)
        mail.transport_encoding = "8bit"
        mail.deliver
      else
        format.html { render :action => "edit" }
        format.xml  { render :xml => @todo.errors, :status => :unprocessable_entity }
      end
    end
  end
  • app/mailers/todo_mail.rb
class TodoMail < ActionMailer::Base
  default :from => "persion@user.com"
  def hello(url)
    @url = url
    mail(:to => "yy@ey-office.com", :subject => "ハロー")
  end
end
  • app/views/todo_mail/hello.text.erb
こんにちは!

<%= @url %>

----------------
テストだよ
  • config/initializers/mailer.rb テスト環境のSMTPにあわせて書いて下さい
ActionMailer::Base.smtp_settings = {
  :delivery_method      => :smtp,
  :raise_delivery_errors=> false,
  :address              => "localhost",
  :port                 => 25,
  :domain               => "test.com"
}

Spec

このサンプルをテストするSpecは以下のように書けます

require 'spec_helper'

describe "Todoの編集" do
  fixtures :todos

  it "編集するとメールが送信される" do
    visit edit_page(todos(:todo01).id)

    within "form" do
      click_button "Update Todo"
    end
    
    unread_emails_for("yy@ey-office.com").size.should == 1
    open_email("yy@ey-office.com").should have_body_text(/こんにちは!/)
  end

  it "メール内のリンクをクリックすると一覧が表示される" do
    mail = open_email("yy@ey-office.com")
    click_email_link_matching(/^http:/, mail)
    page.should have_css("h1", :text => "Listing todos")
  end

end
  • unread_emails_for は未読メール情報の取得
  • open_emailはメールアドレスやサブジェクトを指定して、送信されたメールの内容をテストできます
  • click_email_link_matching は メール内のURL部分を正規表現で指定し、そのURLをクリックした場合のリクエストをアプリに送りアプリのテストを継続します

Email Specのヘルパーメソッドの概要は Email Specページ に書かれていますが、詳細は ヘルパーのソース を見るのが良いと思います :-)