This was born out of a blog post I made a while ago when I went in search of testing that my controller would indeed send an email when I wanted it to. I’m very much a Ruby and Rails newbie (although I am a professional .NET developer by day) so hopefully this will be morphed/corrected/refactored/massively improved over time! If anyone does, i’d love it if you could post a comment on my original blog post. But here’s how I went about testing mailers.
Although Rails generates unit tests for the mailer, I needed tests that would prove that when some action was performed within my application (such as deleting a product etc.) that an email would be sent. Fortunately, this is exactly the kind of thing Mocks are designed for!
Mocks are essentially objects that provide the same public interface as your real object. So, in the case of ActionMailer classes, we want a mock that will have the same interface as our real ActionMailer. The aim being to produce an object that can sit in place of the real one during testing (but not in production) and help us test that our controller is using our mailer correctly.
Although Rails does provide a place for Mocks to go (inside the test directory), in my example I only wanted a mock mailer for testing the interaction with a controller, so my mock mailer was written at the top of my test fixture class.
The example is taken from a real site I was producing, in which I wanted to ensure a receipt would be emailed to the customer upon them completing the order process. My current mailer unit test looked as follows:
def test_receipt @expected.subject = ‘Sureboss Receipt’ @expected.body = read_fixture(’receipt’) @expected.date = Time.nowexpected.from = "<a href="mailto:infosureboss.com">info@sureboss.com"
@expected.to = @order.customer.email_address
assert_equal @expected.encoded,
StoreMailer.create_receipt(
@order,
@expected.date
).encoded
end
As you can see, a `receipt` email method was created, and the test ensures that the rendered message is the same as the text in the fixture.
Now I want to make sure that when some action is performed on my controller, the mailer’s `deliver_test_receipt` method is called — proving that my controller is doing it’s thing right and sending an email under the correct circumstance.
So, I write a test for my controller that I expect to pass:
def test_email_sent_to_customer_when_order_posted @request.session[:logged_in] = “true” order = Order.new(:payment_result => "open") order.save! assert_equal false, StoreMailer.posted post :mark_as_posted, :id => order.id assert_equal true, StoreMailer.posted endOf course, I don’t have a mock mailer yet (just the one rails created for me) so I need to start on the mock mailer. I don’t need any of the email support left in, I just care about the interaction — that the controller asks the mailer to deliver a receipt (note that most dynamic mocking frameworks such as NMock, jMock etc. also check the type and content of parameters).
So, at the top of the test fixture class I add the following to create a new (mock) mailer for the purpose of this test:
class StoreMailer def self.deliver_posted_receipt(email_address, order, time) @posted = true end def self.posted @posted end endUnfortunately, the first time this ran I ran into a problem — it was used in more than one test, and posted was returning true for the second test (when it shouldn’t have been), so, I added a `reset_posted` method that cleared `@posted`.
And all is well. So there you have it, an example of how to test interaction in Rails, and in particular, how I went about testing controller and mailer interaction.
Finally, for anyone interested in mocks, I can highly recommend Steve Freeman, Nat Pryce, Tim Mackinnon, and Joe Walnes’ excellent Mock Roles not Objects paper.