Rails 使用 RSpec 在 Rails 5 下测试邮件的发送

wootaw · May 22, 2017 · Last by wootaw replied at May 24, 2017 · 2739 hits

假设有这样的电子邮件:

class UserMailer < ApplicationMailer
  def welcome_email(user)
    @user = user
    mail(to: @user.email, subject: 'Welcome')
  end
end

然后这样发邮件:

class User < ApplicationRecord
  def self.register(email, password)
    user = create(email: email, password: password)
    UserMailer.welcome_email(user).deliver_later
  end
end

使用 deliver_later 方法需要在 application.rb 里加上:

config.active_job.queue_adapter = :sidekiq # 可以换成其它的 adapter

如果邮件模板中有外链,还需要在 environments/test.rb 里加上:

config.action_mailer.default_url_options = { host: 'test.mysite.com' }

在 user_spec.rb 里:

require 'rails_helper'

RSpec.describe User, type: :model do

  describe '.register' do
    include ActiveJob::TestHelper

    it 'should be created mail job' do
      ActiveJob::Base.queue_adapter = :test
      expect {
        User.register('[email protected]', '123123')
      }.to have_enqueued_job.on_queue('mailers')
    end

    it 'should be sent welcome mail' do
      expect {
        perform_enqueued_jobs do
          User.register('[email protected]', '123123')
        end
      }.to change { ActionMailer::Base.deliveries.size }.by(1)
    end

    it 'should be sent to the right user' do
      perform_enqueued_jobs do
        User.register('[email protected]', '123123')
      end

      mail = ActionMailer::Base.deliveries.last
      expect(mail.to[0]).to eq "[email protected]"
    end
  end
end

我一般这样

module MailSupport
  def stub_email(mailer_class, *methods)
    delivery = instance_double(ActionMailer::MessageDelivery)
    allow(delivery).to receive(:deliver_later)

    methods.each do |method|
      allow(mailer_class).to receive(method).with(any_args).and_return(delivery)
    end
  end
end

RSpec.configure do |config|
  config.include MailSupport, mail: true
end

然后用的时候

stub_email(XXXMailer, :ooxx)
# ...
expect(XXXMailer).to receive(:ooxx).with(:oo, :xx).once

不知道大家一般用什么方法。

感觉框架提供的功能不用再测了,测试参数什么的没问题,邮件内容正确就好了吧

Reply to lithium4010

这个例子中的业务逻辑比较简单。但在比较复杂的业务逻辑中,对于邮件是否发送,是否发送给正确的用户,如果没测试,我始终感觉不踏实。

Reply to wootaw

如果是复杂的业务逻辑,应该针对逻辑写测试

有类拟这样一个需求:按一组邮件地址加好友,其中没有注册系统用户的邮件地址,需要发邮件邀请注册用户。 那么这样的代码:

def invite(mails)
  signed_users = User.where(email: mails)
  add_friends(signed_users)

  (mails - signed_users.map(&:email)).each do |mail|
    UserMailer.invite_email(mail).deliver_later
  end
end

就需要把个方法拆开成便于测试的两个方法来实现,是这样吗? 我就是因为这样的代码不知道怎么测试,才有了上面的折腾,请指教。

You need to Sign in before reply, if you don't have an account, please Sign up first.