Tag Archives: rails3

How to test mailers in Rails 3 / 4 with RSpec

The ActionMailer module has been reconstructed in Rails 3 and the way to test it now is a little different. Now, the mailers has itself subdirectory (app/) and it’s look more like the controllers.

Assuming we already have a mailer to send same information:

class Notifier < ActionMailer::Base
  default from: 'noreply@company.com'

  def instructions(user)
    @name = user.name
    @confirmation_url = confirmation_url(user)
    mail to: user.email, subject: 'Instructions'
  end
end

To send an email through a method from User class:

class User
  def send_instructions
    Notifier.instructions(self).deliver
  end
end

Before test it, make sure the config/environments/test.rb file has the configuration below:

AppName::Application.configure do
  config.action_mailer.delivery_method = :test
end

It ensures that the emails won’t be sent, but stored on ActionMailer::Base.deliveries array. After that (or before), create the tests:

spec/models/user_spec.rb

require 'spec_helper'

describe User do
  let(:user) { User.make }

  it "sends an email" do
    expect { user.send_instructions }.to change { ActionMailer::Base.deliveries.count }.by(1)
  end
end

spec/mailers/notifier_spec.rb

require 'spec_helper'

describe Notifier do
  describe 'instructions' do
    let(:user) { mock_model User, name: 'Lucas', email: 'lucas@email.com' }
    let(:mail) { Notifier.instructions(user) }

    it 'renders the subject' do
      expect(mail.subject).to eql('Instructions')
    end

    it 'renders the receiver email' do
      expect(mail.to).to eql([user.email])
    end

    it 'renders the sender email' do
      expect(mail.from).to eql(['noreply@company.com'])
    end

    it 'assigns @name' do
      expect(mail.body.encoded).to match(user.name)
    end

    it 'assigns @confirmation_url' do
      expect(mail.body.encoded).to match("http://aplication_url/#{user.id}/confirmation")
    end
  end
end

In conclusion, Rails 3 / 4 allows you to create very comprehensive tests for mailers without using any gems to facilitate such tests, which were common in Rails 2.

Como testar mailers no Rails 3 com RSpec

O módulo ActionMailer foi refeito no Rails 3 e a forma de testá-los é um pouco diferente. Agora os mailers ganharam um subdiretório próprio (dentro do app/) e estão mais parecido com controllers.

Supondo que temos um mailer pra envio de uma informação qualquer:

class Notifier < ActionMailer::Base
  default :from => 'noreply@empresa.com'

  def instructions(user)
    @name = user.name
    @confirmation_url = confirmation_url(user)
    mail :to => user.email, :subject => 'Instruções'
  end
end

Para enviar um email através de um método da classe User:

class User
  def send_instructions
    Notifier.instructions(self).deliver
  end
end

Antes de testá-lo, garanta que o arquivo config/environments/test.rb esteja configurado assim:

AppName::Application.configure do
  config.action_mailer.delivery_method = :test
end

Isso garante que os e-mails não serão realmente enviados, e sim armazenamos no array ActionMailer::Base.deliveries. Verificado isso, basta criar os testes:

spec/models/user_spec.rb

require 'spec_helper'

describe User do
  before :each do
    @user = User.make
  end

  it "sends a e-mail" do
    @user.send_instructions
    ActionMailer::Base.deliveries.last.to.should == [@user.email]
  end
end

spec/mailers/notifier_spec.rb

require 'spec_helper'

describe Notifier do
  describe 'instructions' do
    let(:user) { mock_model(User, :name => 'Lucas', :email => 'lucas@email.com') }
    let(:mail) { Notifier.instructions(user) }

    #garantir que o assunto esteja correto
    it 'renders the subject' do
      mail.subject.should == 'Instruções'
    end

    #garantir que o destinatário esteja correto
    it 'renders the receiver email' do
      mail.to.should == [user.email]
    end

    #garantir que o remetente esteja correto
    it 'renders the sender email' do
      mail.from.should == ['noreply@empresa.com']
    end

    #garantir que a variável @name aparece no corpo do e-mail
    it 'assigns @name' do
      mail.body.encoded.should match(user.name)
    end

    #garantir que a variável @confirmation_url aparece no corpo do e-mail
    it 'assigns @confirmation_url' do
      mail.body.encoded.should match("http://aplication_url/#{user.id}/confirmation")
    end
  end
end

Concluíndo, Rails 3 possibilita criar testes bastante abrangentes para mailers sem utilizar plugins ou gems pra facilitar tais testes, os quais eram comuns no Rails 2.