3
votes

I am having a slightly odd problem with sending mail in test mode with Rails 3

It seems that my mailers are not returning anything. For example I have a mailer called UserMailer. Users can make changes that require approval in the app so this has a method called changes_approved that should send the user an email notifying them that their changes have been approved.class

UserMailer < ActionMailer::Base

  default :from => "[email protected]"

  def changes_approved(user, page)

    @user = user
    @page = page

    mail(:to => user.email, :subject => "Your changes have been approved")

  end

end

In my controller I have the following line

UserMailer.changes_approved(@page_revision.created_by, @page_revision.page).deliver

However my tests fail at this point with the error:

undefined method `deliver' for nil:NilClass

When I trigger the same actions on the development site tho (http://localhost:3000 through a browser), the emails are sent out correctly and everything works quite happily

And to add further confusion, I am using devise for authentication and the emails for that seem to be working correctly both in test and development modes. Certainly I am not getting this same error and according to my email-spec tests, everythings working

So this leads me to believe that I have a problem with my mailers rather than my test mail config per se but I have no idea what. Any suggestions would be much appreciated

Thanks

4

4 Answers

10
votes

I used https://gist.github.com/1031144 to convert

# Rails 2 method:
UserMailer.should_receive(:deliver_signup)

to

# Cumbersome Rails 3 method:
mailer = mock
mailer.should_receive(:deliver)
UserMailer.should_receive(:signup).and_return(mailer)
5
votes

I had a similar problem - probably the UserMailer.changes_approved method is being replaced with a mock method, which returns nil (I wasn't using shoulda for that test, but that's my best guess).

My code looked like this (modified to use your example):

UserMailer.expects(:changes_approved).once

I fixed it with an additional stub:

@mailer = stub(:deliver)
UserMailer.expects(:changes_approved).once.returns(@mailer)

The nil is now replaced with @mailer.

2
votes

To test the delayed action mailer we need to first change the configuration of delayed_job (in config/initializers/delayed_job_config.rb) to

Delayed::Worker.delay_jobs = !Rails.env.test?

and in your tests the expectation should be set to

mock_mail = mock(:mail)
mock_mail.should_receive(:deliver)
UserMailer.should_receive(:changes_approved).with(user, page).and_return(mock_mail)
1
votes

Well I have found the answer,

it looks like the problem was in the way I was testing these mailers. In each of the controller tests I had a line similar to

UserMailer.should_receive(:changes_approved).with(user, page)

Whilst this test was passing fine, it appeared to break the mailer itself. I have removed this line from the tests and now they pass ok. Subsequent tests against ActionMailer::Base.deliveries.last to check the details of the sent email are correct appear to be ok so I am happy that this line is not neccessary.

If anyone has an explanation as to why this breaks tho, I would be interested to find out

Thanks anyways