Ruby on Rails – 在模型中将邮件发送作为回调测试

huangapple go评论66阅读模式
英文:

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=&gt;#&lt;User id: 5, email: &quot;factory10@test.io&quot;, created_at: &quot;2023-02-18 01:09:34.878424000 +0000&quot;, ...878424000 +0000&quot;, jti: &quot;2163d284-1349-4e48-8a2a-1b52b578921c&quot;, username: &quot;jose_test10&quot;, icon_id: 5&gt;})
       expected: 1 time with arguments: ({:user=&gt;#&lt;User id: 5, email: &quot;factory10@test.io&quot;, created_at: &quot;2023-02-18 01:09:34.878424000 +0000&quot;, ...878424000 +0000&quot;, jti: &quot;2163d284-1349-4e48-8a2a-1b52b578921c&quot;, username: &quot;jose_test10&quot;, icon_id: 5&gt;})
       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 &#39;#save&#39; do
  subject(:user) { build(:valid_user) }

  let(:parameterized_mailer) { instance_double(&#39;ActionMailer::Parameterized::Mailer&#39;) }
  let(:parameterized_message) { instance_double(&#39;ActionMailer::Parameterized::MessageDelivery&#39;) }

  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 &#39;when user is created&#39; do
    it &#39;sends a welcome email&#39; 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.

huangapple
  • 本文由 发表于 2023年2月18日 09:11:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/75490557.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定