lost9123193
lost9123193

Reputation: 11040

Best way to test a mailer with arguments

I have a mailer that passes an argument like so:

AnimalMailer.daily_message(owner).deliver_later

The method looks like this:

AnimalMailer

class AnimalMailer < ApplicationMailer

  def daily_message(owner)
     mail(
      to: "#{user.name}",
      subject: "test",
      content_type: "text/html",
      date: Time.now.in_time_zone("Mountain Time (US & Canada)")
    )
  end
end

I'm new to writing specs and was wondering how should I pass the owner to the method and test it. I currently have this set up:

require "rails_helper"

RSpec.describe AnimalMailer, type: :mailer do
  
  describe "monthly_animal_message" do
    let(:user) { create(:user, :admin) }

    it "renders the headers" do
      expect(mail.subject).to eq("test")
      expect(mail.to).to eq(user.name)
    end
  end
end

Upvotes: 3

Views: 3627

Answers (2)

ewertoncodes
ewertoncodes

Reputation: 486

Before testing it, make sure the config / environment / test.rb file is set to:

  config.action_mailer.delivery_method = :test

This ensures that emails are not actually sent, but are stored in the ActionMailer :: Base.deliveries array.

Following Four-Phase Test :

animal_mailer.rb

class AnimalMailer < ApplicationMailer
  default from: 'noreply@animal_mailer.com'

  def daily_message(owner)
    @name = owner.name

    mail(
      to: owner.email,
      subject: "test",
      content_type: "text/html",
      date: Time.now.in_time_zone("Mountain Time (US & Canada)")
    )
  end
end

animal_mailer_spec.rb

RSpec.describe AnimalMailer, type: :mailer do
  describe 'instructions' do
    let(:user) { create(:user, :admin) }
    let(:mail) { described_class.daily_message(user).deliver_now }

    it 'renders the subject' do
      expect(mail.subject).to eq("test")
    end

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

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

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

if you have a model user:

class User
  def send_instructions
    AnimalMailer.instructions(self).deliver_now
  end
end


RSpec.describe User, type: :model do
  subject { create :user }

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

Upvotes: 3

rmlockerd
rmlockerd

Reputation: 4136

Specs generally follow a three-step flow 1) set up, 2) invoke, 3) expect. This applies for unit testing mailers like anything else. The invocation and parameters are the same in the test as for general use, so in your case:

RSpec.describe AnimalMailer, type: :mailer do  
  describe "monthly_campaign_report" do
    let(:user) { create(:user, :admin) }
    let(:mail) { described_class.daily_message(user) }  # invocation

    it 'renders the headers' do
      expect(mail.subject).to eq('test')
      expect(mail.to).to eq(user.name)
    end

    it 'renders the body' do
      # whatever
    end
  end
end

Note that since the describe is the class name being tested, you can use described_class from there to refer back to the described class. You can always use AnimalMailer.daily_message as well, but among other things described_class ensures that if you shuffle or share examples that you are always testing what you think you are.

Also note that in the case of unit testing a mailer, you're mostly focused on the correct generation of the content. Testing of successful delivery or use in jobs, controllers, etc., would be done as part of request or feature tests.

Upvotes: 5

Related Questions