英文:
Ruby on Rails - Testing Mailer as a callback in Model
问题
以下是您要翻译的代码部分:
我有这个逻辑,当用户被创建时,我会发送欢迎邮件给用户。
`User.rb`
```ruby
class User < ApplicationRecord
# Callbacks
after_create :send_registration_email
private
def send_registration_email
UserConfirmationMailer.with(user: self).user_registration_email.deliver_later
end
end
我尝试在 user_spec.rb
中进行测试:
describe "#save" do
subject { create :valid_user }
context "when user is created" do
it "receives welcome email" do
mail = double('Mail')
expect(UserConfirmationMailer).to receive(:user_registration_email).with(user: subject).and_return(mail)
expect(mail).to receive(:deliver_later)
end
end
end
但是它不起作用。我收到以下错误:
Failure/Error: expect(UserConfirmationMailer).to receive(:user_registration_email).with(user: subject).and_return(mail)
(UserConfirmationMailer (class)).user_registration_email({:user=>#<User id: 5, email: "factory10@test.io", created_at: "2023-02-18 01:09:34.878424000 +0000", ...878424000 +0000", jti: "2163d284-1349-4e48-8a2a-1b52b578921c", username: "jose_test10", icon_id: 5>})
expected: 1 time with arguments: ({:user=>#<User id: 5, email: "factory10@test.io", created_at: "2023-02-18 01:09:34.878424000 +0000", ...878424000 +0000", jti: "2163d284-1349-4e48-8a2a-1b52b578921c", username: "jose_test10", icon_id: 5>})
received: 0 times
在测试回调中发送邮件的操作时,我做错了什么吗?
附加信息:
我的 environment/test.rb
配置如下:
config.action_mailer.delivery_method = :test
config.active_job.queue_adapter = :test
config.action_mailer.default_url_options = { :host => "http://localhost:3000" }
即使我按照此链接更改 config.active_job.queue_adapter
为 :test
,我仍然收到相同的错误。
我尝试的另一种方法如下:
expect { FactoryBot.create(:valid_user) }.to have_enqueued_job(ActionMailer::MailDeliveryJob).with('UserConfirmationMailer', 'user_registration_email', 'deliver_now', subject)
但是 subject
和在 FactoryBot.create(:valid_user)
中创建的用户是不同的。
欢迎提供任何想法。谢谢!
<details>
<summary>英文:</summary>
I have this logic where I send a welcome email to the user when it is created.
`User.rb`
class User < ApplicationRecord
Callbacks
after_create :send_registration_email
private
def send_registration_email
UserConfirmationMailer.with(user: self).user_registration_email.deliver_later
end
end
I tried testing it in `user_spec.rb`:
describe "#save" do
subject { create :valid_user }
context "when user is created" do
it "receives welcome email" do
mail = double('Mail')
expect(UserConfirmationMailer).to receive(:user_registration_email).with(user: subject).and_return(mail)
expect(mail).to receive(:deliver_later)
end
end
end
But it is not working. I get this error:
Failure/Error: expect(UserConfirmationMailer).to receive(:user_registration_email).with(user: subject).and_return(mail)
(UserConfirmationMailer (class)).user_registration_email({:user=>#<User id: 5, email: "factory10@test.io", created_at: "2023-02-18 01:09:34.878424000 +0000", ...878424000 +0000", jti: "2163d284-1349-4e48-8a2a-1b52b578921c", username: "jose_test10", icon_id: 5>})
expected: 1 time with arguments: ({:user=>#<User id: 5, email: "factory10@test.io", created_at: "2023-02-18 01:09:34.878424000 +0000", ...878424000 +0000", jti: "2163d284-1349-4e48-8a2a-1b52b578921c", username: "jose_test10", icon_id: 5>})
received: 0 times
Am I doing something wrong when testing the action of sending the email in a callback?
PD.
My `environment/test.rb` config is as follows:
config.action_mailer.delivery_method = :test
config.active_job.queue_adapter = :test
config.action_mailer.default_url_options = { :host => "http://localhost:3000" }
Even if I change `config.active_job.queue_adapter` to `:test` following [this][1], I get the same error.
Another way I am trying to do it is like this:
expect { FactoryBot.create(:valid_user) }.to have_enqueued_job(ActionMailer::MailDeliveryJob).with('UserConfirmationMailer', 'user_registration_email', 'deliver_now', subject)
But then `subject` and the user created in `FactoryBot.create(:valid_user)` is different..
Any ideas are welcome. Thank you!
[1]: https://dev.to/jbranchaud/test-actionmailer-deliverlater-in-rspec-controller-tests-44h7
</details>
# 答案1
**得分**: 2
以下是翻译的内容:
问题中的存根不起作用,因为它没有按照它们在实现中被调用的顺序存根方法链。
当您想要存根此方法链时:
UserConfirmationMailer.with(user: self).user_registration_email.deliver_later
然后您必须先存根`with`调用,然后是`user_registration_email`,最后是`deliver_later`。
```ruby
describe '#save' do
subject(:user) { build(:valid_user) }
let(:parameterized_mailer) { instance_double('ActionMailer::Parameterized::Mailer') }
let(:parameterized_message) { instance_double('ActionMailer::Parameterized::MessageDelivery') }
before do
allow(UserConfirmationMailer).to receive(:with).and_return(parameterized_mailer)
allow(parameterized_mailer).to receive(:user_registration_email).and_return(parameterized_message)
allow(parameterized_message).to receive(:deliver_later)
end
context 'when user is created' do
it 'sends a welcome email' do
user.save!
expect(UserConfirmationMailer).to have_received(:with).with(user: user)
expect(parameterized_mailer).to have_received(:user_registration_email)
expect(parameterized_message).to have_received(:deliver_later)
end
end
end
注意:我不确定在这种情况下是否可以使用instance_double
,因为Parameterized
在内部使用method_missing
。虽然通常首选instance_double
,但您可能需要改用double
。
英文:
The stubbing in the question doesn't work because it doesn't stub the chain of methods in the same order then they are called in the implementation.
When you want to stub this method chain
UserConfirmationMailer.with(user: self).user_registration_email.deliver_later
then you have to first stub the with
call, then the user_registration_email
and last the deliver_later
.
describe '#save' do
subject(:user) { build(:valid_user) }
let(:parameterized_mailer) { instance_double('ActionMailer::Parameterized::Mailer') }
let(:parameterized_message) { instance_double('ActionMailer::Parameterized::MessageDelivery') }
before do
allow(UserConfirmationMailer).to receive(:with).and_return(parameterized_mailer)
allow(parameterized_mailer).to receive(:user_registration_email).and_return(parameterized_message)
allow(parameterized_message).to receive(:deliver_later)
end
context 'when user is created' do
it 'sends a welcome email' do
user.save!
expect(UserConfirmationMailer).to have_received(:with).with(user: user)
expect(parameterized_mailer).to have_received(:user_registration_email)
expect(parameterized_message).to have_received(:deliver_later)
end
end
end
Note: I am not sure if using instance_double
will work in this case, because the Parameterized
uses method_missing
internally. Although instance_double
is usually preferred, you might need to use double
instead.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论