Reputation: 19873
I have an RSpec test like this:
it "should ..." do
# mailer = mock
# mailer.should_receive(:deliver)
Mailer.should_receive(:notification_to_sender)#.and_return(mailer)
visit transactions_path
expect do
page.should_not have_css("table#transactions_list tbody tr")
find('#some_button').click
page.should have_css("table#transactions_list tbody tr", :count => 1)
end.to change{Transaction.count}.by(1)
end
If I remove the commented pieces at the top, the test passes. But with the commented sections in place (how I'd expect to write it) the test fails.
I got the commented pieces off some of googling around the net, but I don't really understand what it's doing or why this fixes it. It seems like there should be a cleaner way to test emails without this.
Can anyone shed some light? Thanks!
I'm using rails 3 and rspec-rails 2.10.1
Upvotes: 3
Views: 5496
Reputation: 752
You're likely calling Mailer.notification_to_sender.deliver
in your controller, or better yet, a background job. I'm guessing notification_to_sender probably takes a parameter as well.
Anyways, when you call the notification_to_sender
method on Mailer you're getting back an instance of Mail::Message
that has the deliver
method on it. If you were simply doing Mailer.notification_to_sender
without also calling deliver
, you could run what you have there with the comments and all would be fine. I would guess you're also calling deliver
though.
In that case your failure message would be something like
NoMethodError:
undefined method `deliver' for nil:NilClass
That is because nil
is Ruby's default return value much of the time, which Rails also inherits. Without specifying the mailer = mock
and .and_return(mailer)
parts, when the controller executes in context of the test then notification_to_sender will return nil and the controller will try to call deliver
on that nil object.
The solution you have commented out is to mock out notification_to_sender
's return value (normally Mail::Message
) and then expect that deliver
method to be called on it.
Upvotes: 2
Reputation: 3529
I think you want an instance of Mailer to receive notification_to_sender not the class. From the Rails API
You never instantiate your mailer class. Rather, your delivery instance methods are automatically wrapped in class methods that start with the word deliver_ followed by the name of the mailer method that you would like to deliver. The signup_notification method defined above is delivered by invoking Notifier.deliver_signup_notification.
Therefore I would use
Mailer.any_instance.should_receive(:notification_to_sender)
Also, if you need to get the last delivered message, use
ActionMailer::Base.deliveries.last
I think that should solve your problem.
Upvotes: 2