I have an existing method for a Post model that looks like this:
def send_update_email
PostEmail.deliver(self)
end
with the corresponding, passing rspec test:
context 'send_update_email' do
it 'delegates to PostEmail' do
post = create_post #separate method that uses factory girl
expect(PostEmail).to receive(:deliver).with(post)
post.send_update_email
end
end
The PostEmail class has this class method:
def self.deliver(post)
new(post).deliver
end
This looks to me like it’s just initiating the instance and then calling the ‘deliver’ instance method.
I would like to remove that class method in PostEmail so I can rewrite the original method like this:
def send_update_email
PostEmail.new(self).deliver
end
but I can’t figure out how to rewrite the test to get it to pass.
I don’t really understand how the existing test is working since wouldn’t you usually would use the ‘receive’ method on a mock? In this case it’s just using it on PostEmail.
Any ideas on what I should do here to get this refactoring to work?
You can build a double that stands in for the PostEmail instance, and then stub PostEmail so that .new returns the double rather than a PostEmail:
it 'delivers an email with the post' do
post_email_double = double('post email double', deliver: true)
allow(PostEmail).to receive(:new) { post_email_double }
post = create_post
post.send_update_email
expect(PostEmail).to have_received(:new).with(post) # want to verify the post was used
expect(post_email_double).to have_received(:deliver) # want to verify that deliver was called
end
I like putting the expectations at the end rather than before the test so that I can keep the test flow in setup, exercise, then verification in that order.
That’s correct. In order to do this in RSpec you used to have to use a gem like Bourne, but the functionality was merged into RSpec 2 at some point last year. I think having the expectations always fall at the end makes reading tests easier, as you never have to check back for expectations earlier than the test exercise.