1
votes

My question is about how to spec a callback for sending an email with the right way.

I have a model called Question. After a question is created, I have a callback after_create to send an email.

I would like to create a rpsec to test this method. However, I already have specs for each Mailer, that verifies if the email is actually sent and the recipients.

So, I would like to know if inside the after_create should I test the call of the send_mail method or check if the last email sent is present? Because, like I said, the test for email itself is in the spec for the mailer spec/mailers/question_mailer_spec.rb

Model:

class Question < ActiveRecord::Base

after_create :send_email

def send_email
   QuestionMailer.send_email(:questioned, self.id)
end

Rspec:

describe "#send_email" do
  it "should sends email after create" do
     expect(QuestionMailer).to receive(:send_email).with(:questioned, **question.id**) 
     question = create(:question)

  end
end

Thanks!

1

1 Answers

5
votes

You should only test the call to send_email.

You want to assert the behaviour of your implementation, and not the implementation itself. In this case, the message sent from Question to its collaborator QuestionMailer. Remember, test the interface, and not the implementation details.

Question does not have a clue how an email is sent. It delegate to QuestionMailer. So it makes no sense to check for the last email sent, email queue, and whatnot.

I suggest you to take the time to watch the great presentation Sandi Metz gave on RailsConf 2013: The Magic Tricks of Testing. The slides are here.

Best!

UPDATE: how to handle the self.id?

I think it would be better if you pass self rather than self.id. Besides solving your problem, it:

  • makes your code more expressive;
  • avoids using primitive values (integer);
  • will probably prevent at least one more database read in your production code.

In case you really want/need to stick with self.id, you could stub any value in the Question instance being tested. Keep in mind that you're unit testing #send_mail, so it really doesn't matter if the value of id is stubbed or generated by the database. And the less you hit the database, faster your tests will run.