Tag Archives: rails3

How to test Rails mailers using 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'

RSpec.describe User do
  subject { create :user }

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

spec/mailers/notifier_spec.rb

require 'spec_helper'

RSpec.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 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.