fossil12
fossil12

Reputation: 547

How to test with RSpec if an email is delivered

I'd like to test if an email is delivered if I call a controller method with :post. I'll use email_spec so I tried this snipped here: http://rubydoc.info/gems/email_spec/1.2.1/file/README.rdoc#Testing_In_Isolation

But it doesn't work, because I pass an instance of the model-object to the delivery-method and the instance is saved before the delivery.

I tried to create an other instance of the model-object, but then the id isn't the same.

My controller-method looks like this:

def create

   @params = params[:reservation]

   @reservation = Reservation.new(@params)
   if @reservation.save
      ReservationMailer.confirm_email(@reservation).deliver
      redirect_to success_path
   else
      @title = "Reservation"
      render 'new'
   end

end

Do you have any idea to solve this?

Upvotes: 54

Views: 46781

Answers (8)

Alter Lagos
Alter Lagos

Reputation: 12540

Anyone using rspec +3.4 and ActiveJob to send async emails, try with:

expect {
  post :create, params
}.to have_enqueued_job.on_queue('mailers')

Upvotes: 9

GuilPejon
GuilPejon

Reputation: 1021

If you're using Capybara with Capybara Email and you sent an email to [email protected], you can also use this method:

email = open_email('[email protected]')

And then you can test it like this:

expect(email.subject).to eq('SUBJECT')
expect(email.to).to eq(['[email protected]'])

Upvotes: 1

Developer
Developer

Reputation: 6223

Try email-spec

describe "POST /signup (#signup)" do
  it "should deliver the signup email" do
    # expect
    expect(UserMailer).to(receive(:deliver_signup).with("[email protected]", "Jimmy Bean"))
    # when
    post :signup, "Email" => "[email protected]", "Name" => "Jimmy Bean"
  end
end

more examples here: https://github.com/email-spec/email-spec#testing-in-isolation

Upvotes: 0

boblin
boblin

Reputation: 3541

This is way how to test that Mailer is called with right arguments. You can use this code in feature, controller or mailer spec:

delivery = double
expect(delivery).to receive(:deliver_now).with(no_args)

expect(ReservationMailer).to receive(:confirm_email)
  .with('reservation')
  .and_return(delivery)

Upvotes: 11

Dennis
Dennis

Reputation: 59449

Configure your test environment to accumulate sent mails in ActionMailer::Base.deliveries.

# config/environments/test.rb
config.action_mailer.delivery_method = :test

Then something like this should allow you to test that the mail was sent.

# Sample parameters you would expect for POST #create.
def reservation_params
  { "reservation" => "Drinks for two at 8pm" }
end

describe MyController do
  describe "#create" do
    context "when a reservation is saved" do
      it "sends a confirmation email" do
        expect { post :create, reservation_params }.to change { ActionMailer::Base.deliveries.count }.by(1)
      end
    end
  end
end

Note that my example uses RSpec 3 syntax.

Upvotes: 35

Jules Copeland
Jules Copeland

Reputation: 1722

I know I'm late to the party with this one, but for future Googlers...

I think a better solution to this problem is answered here

The previously accepted answer is testing the Mailer itself (inside the controller spec). All you should be testing for here is that the Mailer gets told to deliver something with the right parameters.

You can then test the Mailer elsewhere to make sure it responds to those parameters correctly.

ReservationMailer.should_receive(:confirm_email).with(an_instance_of(Reservation))

Upvotes: 19

Jared
Jared

Reputation: 3005

To add a little more, make sure if you're going to stub out a call using should_receive that you have an integration test elsewhere testing that you're actually calling the method correctly.

I've been bit a few times by changing a method that was tested elsewhere with should_receive and having tests still pass when the method call was broken.

If you prefer to test the outcome rather than using should_receive, shoulda has a nice matcher that works like the following:

it { should have_sent_email.with_subject(/is spam$/) }

Shoulda documentation

More information on using Shoulda Matchers with rSpec

Upvotes: 1

Chowlett
Chowlett

Reputation: 46657

Assuming your test environment is set up in the usual fashion (that is, you have config.action_mailer.delivery_method = :test), then delivered emails are inserted into the global array ActionMailer::Base.deliveries as Mail::Message instances. You can read that from your test case and ensure the email is as expected. See here.

Upvotes: 77

Related Questions