Ruby on Rails:测试Model回调中的邮件发送功能遇阻求助
测试用户创建时邮件发送回调的问题排查与解决
第一个测试失败的原因及修复
你的第一个测试存在两个核心错误:
- 用户创建时机错误:
subject { create :valid_user }会在测试例子执行前就完成用户创建,此时after_create回调已经触发,邮件发送逻辑已执行完毕,后续的expect断言根本捕获不到目标方法调用。 - 方法调用逻辑误解:你的邮件发送代码是
UserConfirmationMailer.with(user: self).user_registration_email.deliver_later,实际调用流程是:先调用类方法UserConfirmationMailer.with(user: self)返回一个mailer实例,再调用该实例的user_registration_email方法,而非直接调用UserConfirmationMailer类的user_registration_email方法。你原来的断言错误地期望类方法被调用。
修复后的测试代码:
describe "#save" do subject { build :valid_user } # 用build创建未持久化的实例,避免提前触发回调 context "when user is created" do it "triggers welcome email delivery" do mail_double = double('Mail') # 先断言with方法的调用,再返回包含user_registration_email方法的实例double expect(UserConfirmationMailer).to receive(:with).with(user: subject) .and_return(double(user_registration_email: mail_double)) expect(mail_double).to receive(:deliver_later) subject.save! # 手动触发持久化,触发after_create回调 end end end
第二个测试的问题及修复
你用have_enqueued_job时,subject是预先创建的用户实例,而FactoryBot.create(:valid_user)会生成一个全新的用户实例,两者内存地址不同,导致断言匹配失败。
可以通过以下两种方式修复:
方式1:匹配实例类型而非具体实例
it "enqueues the welcome email job" do expect { FactoryBot.create(:valid_user) }.to have_enqueued_job(ActionMailer::MailDeliveryJob) .with('UserConfirmationMailer', 'user_registration_email', 'deliver_now', kind_of(User)) end
方式2:捕获创建的用户实例,精确匹配属性
it "enqueues the welcome email job for the created user" do user = nil expect { user = FactoryBot.create(:valid_user) }.to have_enqueued_job(ActionMailer::MailDeliveryJob) .with('UserConfirmationMailer', 'user_registration_email', 'deliver_now', a_hash_including(user: user)) end
额外注意事项
- 你的
environment/test.rb配置是正确的,:test队列适配器会将job存入内存队列,方便测试断言,无需调整。 - 测试邮件发送时,也可以直接用Rails提供的
ActionMailer::Base.deliveries数组验证邮件是否生成,比如:
it "sends welcome email after creation" do expect { FactoryBot.create(:valid_user) }.to change(ActionMailer::Base.deliveries, :count).by(1) last_email = ActionMailer::Base.deliveries.last expect(last_email.to).to include(FactoryBot.create(:valid_user).email) end
内容的提问来源于stack exchange,提问作者josegp
相关产品推荐
相关产品推荐

