83

delay_job_active_record を使用して、Rails 4.2 にアップグレードしようとしています。ジョブがすぐに実行されるように、テスト環境用のdelayed_job バックエンドを設定していません。

RSpec で新しい「deliver_later」メソッドをテストしようとしていますが、方法がわかりません。

古いコントローラー コード:

ServiceMailer.delay.new_user(@user)

新しいコントローラー コード:

ServiceMailer.new_user(@user).deliver_later

私はそれを次のようにテストするために使用しました:

expect(ServiceMailer).to receive(:new_user).with(@user).and_return(double("mailer", :deliver => true))

今、私はそれを使用してエラーが発生します。(二重の「メーラー」が予期しないメッセージを受け取りました:deliver_later with (引数なし))

ただ

expect(ServiceMailer).to receive(:new_user)

「nil:NilClass の undefined method `deliver_later'」でも失敗します

ActiveJob で test_helper を使用してジョブがキューに登録されているかどうかを確認できる例をいくつか試しましたが、正しいジョブがキューに登録されていることをテストできませんでした。

expect(enqueued_jobs.size).to eq(1)

これは、test_helper が含まれている場合はパスしますが、送信されている電子メールが正しいかどうかを確認することはできません。

私がやりたいことは次のとおりです。

  • 正しいメールがキューに入れられていることをテストします (またはテスト環境ですぐに実行されます)
  • 正しいパラメータ (@user) を使用

何か案は??ありがとう

4

13 に答える 13

55

ActiveJob とrspec-rails3.4+ を使用すると、次のように使用できますhave_enqueued_job

expect { 
  YourMailer.your_method.deliver_later 
  # or any other method that eventually would trigger mail enqueuing
}.to( 
  have_enqueued_job.on_queue('mailers').with(
    # `with` isn't mandatory, but it will help if you want to make sure is
    # the correct enqueued mail.
    'YourMailer', 'your_method', 'deliver_now', any_param_you_want_to_check
  )
)

また、あなたが持っていることを再確認してconfig/environments/test.rbください:

config.action_mailer.delivery_method = :test
config.active_job.queue_adapter = :test

別のオプションは、インライン ジョブを実行することです。

config.active_job.queue_adapter = :inline

ただし、キューに入れられるとすぐにすべてのジョブが実行されるため、これはテスト スイートの全体的なパフォーマンスに影響することに注意してください。

于 2016-06-29T04:15:55.720 に答える
11

( monkeypatching よりもdeliver_later) より良い解決策は次のとおりです。

require 'spec_helper'
include ActiveJob::TestHelper

describe YourObject do
  around { |example| perform_enqueued_jobs(&example) }

  it "sends an email" do
    expect { something_that.sends_an_email }.to change(ActionMailer::Base.deliveries, :length)
  end
end

これaround { |example| perform_enqueued_jobs(&example) }により、テスト値をチェックする前にバックグラウンド タスクが実行されます。

于 2016-05-31T14:07:23.700 に答える
0

この回答は少し異なりますが、Rails API の新しい変更や、配信方法の変更 (のdeliver_now代わりに使用するなどdeliver_later) などの場合に役立つ場合があります。

ほとんどの場合、私がテストしているメソッドに依存関係としてメーラーを渡すことですが、レールからメーラーを渡すのではなく、「その方法で物事を行うオブジェクト」を渡します私が欲しい"...

たとえば、ユーザーの登録後に正しいメールを送信していることを確認したい場合...

class DummyMailer
  def self.send_welcome_message(user)
  end
end

it "sends a welcome email" do
  allow(store).to receive(:create).and_return(user)
  expect(mailer).to receive(:send_welcome_message).with(user)
  register_user(params, store, mailer)
end

そして、そのメソッドを呼び出すコントローラーで、そのメーラーの「実際の」実装を記述します...

class RegistrationsController < ApplicationController
  def create
    Registrations.register_user(params[:user], User, Mailer)
    # ...
  end

  class Mailer
    def self.send_welcome_message(user)
      ServiceMailer.new_user(user).deliver_later
    end
  end
end

このようにして、正しいデータ (引数) を使用して、正しいメッセージを正しいオブジェクトに送信していることをテストしていると感じています。そして、ロジックを持たない非常に単純なオブジェクトを作成する必要があり、ActionMailer がどのように呼び出されるかを知るだけの責任があります。

私は自分が持っている依存関係をより細かく制御したいので、これを行うことを好みます。これは、「依存性逆転の原則」の例です。

それがあなたの好みかどうかはわかりませんが、問題を解決する別の方法です =)。

于 2018-03-15T03:23:59.237 に答える