Sarah Vessels
Sarah Vessels

Reputation: 31640

ActionMailer::Base.deliveries always empty in RSpec model test in Rails 4

I have the following failing test:

describe Image do
  describe 'a_method' do
    it 'sends email' do
      Image.count.should == 1
      expect do
        ImageMailer.deleted_image(Image.last.id).deliver
      end.to change(ActionMailer::Base.deliveries, :length)
    end
  end
end

And here's my mailer:

class ImageMailer < ActionMailer::Base
  layout 'email'
  default from: '[email protected]'

  def deleted_image image_id, recipient='[email protected]'
    @image = Image.find(image_id)
    subject = "Image email"
    mail(to: recipient, subject: subject) do |format|
      format.text
      format.html { render layout: 'email' }
    end
  end
end

My test fails with Failure/Error: expect do length should have changed, but is still 0. I have another test for my mailer itself and it passes:

describe ImageMailer do
  it 'should deliver the mail' do
    expect do
      subject.deliver
    end.to change { ActionMailer::Base.deliveries.length }.by(1)
  end
end

I don't know why ActionMailer::Base.deliveries is always empty in my model spec but not in my mailer spec. The mail obviously works. My model test was originally different, testing whether a method on my model caused an email to be sent, but when that failed to generate a mail delivery, I explicitly tried the ImageMailer.deleted_image(Image.last.id).deliver line and it didn't work. Is there something special about RSpec tests where the object being described is a mailer class?

Here are some relevant lines from my config/environments/test.rb file:

config.action_mailer.delivery_method = :test
config.action_mailer.default_url_options = {host: 'localhost:3000'}
config.action_mailer.perform_deliveries = true

Upvotes: 1

Views: 2192

Answers (1)

Sarah Vessels
Sarah Vessels

Reputation: 31640

A combination of should_receive with and_return got my test to pass:

it 'send email for an image not in Amazon that is in our table' do
  mailer = double
  mailer.should_receive(:deliver)
  ImageMailer.should_receive(:deleted_image).and_return(mailer)
  ImageMailer.deleted_image(Image.last.id).deliver
end

And when I comment out ImageMailer.deleted_image(Image.last.id).deliver, the test fails as expected. From this, I was able to replace ImageMailer.deleted_image(Image.last.id).deliver with my actual test where I check that calling a method on my model causes an email to be sent.

Upvotes: 2

Related Questions