rspec으로 ActionMailer delivery_later를 테스트하는 방법
delayed_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 (no args))
다만
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)
어떤 아이디어 ?? 감사
내가 당신을 올바르게 이해한다면 다음과 같이 할 수 있습니다.
message_delivery = instance_double(ActionMailer::MessageDelivery)
expect(ServiceMailer).to receive(:new_user).with(@user).and_return(message_delivery)
allow(message_delivery).to receive(:deliver_later)
핵심은 deliver_later
.
이 질문을 찾았지만 단순히 DelayedJob이 아닌 ActiveJob을 사용하고 Rails 5를 사용하고 있다면 ActionMailer를 config/environments/test.rb
다음 에서 구성하는 것이 좋습니다 .
config.active_job.queue_adapter = :inline
(이것은 Rails 5 이전의 기본 동작이었습니다)
ActiveJob 및 rspec 3.4+를 사용하면 다음과 have_enqueued_job
같이 사용할 수 있습니다 .
expect {
YourMailer.your_method.deliver_later
}.to have_enqueued_job.on_queue('mailers')
다른 것 중 어느 것도 나에게 충분하지 않았기 때문에 내 대답을 추가하겠습니다.
1) Mailer를 조롱 할 필요가 없습니다. Rails는 기본적으로 이미 그렇게합니다.
2) 이메일 생성을 실제로 트리거 할 필요가 없습니다. 이렇게하면 시간이 걸리고 테스트 속도가 느려집니다!
그렇기 때문에 environments/test.rb
다음 옵션을 설정해야합니다.
config.action_mailer.delivery_method = :test
config.active_job.queue_adapter = :test
다시 : 사용하여 전자 메일을 배달하지 않습니다 deliver_now
하지만 항상 사용합니다 deliver_later
. 그러면 사용자가 이메일의 효과적인 전달을 기다리지 않습니다. 당신이없는 경우 sidekiq
, sucker_punch
또는 생산 기타, 단순히 사용 config.active_job.queue_adapter = :async
. 그리고 하나 async
또는 inline
개발 환경.
테스트 환경에 대한 다음 구성이 주어지면 이메일은 항상 대기열에 추가되고 전달을 위해 실행되지 않습니다. 이렇게하면 이메일을 모의하는 것을 방지 할 수 있으며 대기열에 올바르게 삽입되었는지 확인할 수 있습니다.
테스트에서는 항상 테스트를 두 개로 나눕니다. 1) 이메일이 올바른 매개 변수로 대기열에 올바로 추가되었는지 확인하기위한 하나의 단위 테스트 2) 제목, 보낸 사람, 수신자 및 콘텐츠가 올바른지 확인하기위한 메일에 대한 하나의 단위 테스트 .
다음 시나리오가 주어지면 :
class User
after_update :send_email
def send_email
ReportMailer.update_mail(id).deliver_later
end
end
이메일이 대기열에 올바르게 추가되었는지 확인하는 테스트를 작성하세요.
include ActiveJob::TestHelper
expect { user.update(name: 'Hello') }.to have_enqueued_job(ActionMailer::DeliveryJob).with('ReportMailer', 'update_mail', 'deliver_now', user.id)
이메일에 대한 별도의 테스트를 작성하십시오.
Rspec.describe ReportMailer do
describe '#update_email' do
subject(:mailer) { described_class.update_email(user.id) }
it { expect(mailer.subject).to eq 'whatever' }
...
end
end
- 이메일이 일반 작업이 아니라 대기열에 추가되었는지 정확히 테스트했습니다.
- 당신의 테스트는 빠릅니다
- 조롱 할 필요가 없습니다
시스템 테스트를 작성할 때 더 이상 속도가 그다지 중요하지 않기 때문에 실제로 이메일을 전달할 것인지 결정하십시오. 개인적으로 다음을 구성하고 싶습니다.
RSpec.configure do |config|
config.around(:each, :mailer) do |example|
perform_enqueued_jobs do
example.run
end
end
end
:mailer
실제로 이메일을 보내고 싶었던 테스트에 속성을 할당합니다 .
Rails에서 이메일을 올바르게 구성하는 방법에 대한 자세한 내용은 https://medium.com/@coorasse/the-correct-emails-configuration-in-rails-c1d8418c0bfd 문서를 참조하세요.
이거 추가 해봐:
# spec/support/message_delivery.rb
class ActionMailer::MessageDelivery
def deliver_later
deliver_now
end
end
참조 : http://mrlab.sk/testing-email-delivery-with-deliver-later.html
더 좋은 해결책 (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) }
보장하지만 백그라운드 작업은 테스트 값을 확인하기 전에 실행됩니다.
나는 같은 의심을 가지고 왔고이 답변에서 영감을 얻은 덜 장황한 (한 줄) 방식으로 해결했습니다.
expect(ServiceMailer).to receive_message_chain(:new_user, :deliver_later).with(@user).with(no_args)
마지막 with(no_args)
은 필수입니다.
그러나 deliver_later
전화를 받고 있는지 신경 쓰지 않으면 다음을 수행하십시오.
expect(ServiceMailer).to expect(:new_user).with(@user).and_call_original
간단한 방법은 다음과 같습니다.
expect(ServiceMailer).to(
receive(:new_user).with(@user).and_call_original
)
# subject
I have come here looking for an answer for a complete testing, so, not just asking if there is one mail waiting to be sent, in addition, for its recipient, subject...etc
I have a solution, than comes from here, but with a little change:
As it says, the curial part is
mail = perform_enqueued_jobs { ActionMailer::DeliveryJob.perform_now(*enqueued_jobs.first[:args]) }
The problem is that the parameters than mailer receives, in this case, is different from the parameters than receives in production, in production, if the first parameter is a Model, now in testing will receive a hash, so will crash
enqueued_jobs.first[:args]
["UserMailer", "welcome_email", "deliver_now", {"_aj_globalid"=>"gid://forjartistica/User/1"}]
So, if we call the mailer as UserMailer.welcome_email(@user).deliver_later
the mailer receives in production a User, but in testing will receive {"_aj_globalid"=>"gid://forjartistica/User/1"}
All comments will be appreciate, The less painful solution I have found is changing the way that I call the mailers, passing, the model's id and not the model:
UserMailer.welcome_email(@user.id).deliver_later
This answer is a little bit different, but may help in cases like a new change in the rails API, or a change in the way you want to deliver (like use deliver_now
instead of deliver_later
).
What I do most of the time is to pass a mailer as a dependency to the method that I am testing, but I don't pass an mailer from rails, I instead pass an object that will do the the things in the "way that I want"...
For example if I want to check that I am sending the right mail after the registration of a user... I could do...
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
And then in the controller where I will be calling that method, I would write the "real" implementation of that mailer...
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
In this way I feel that I am testing that I am sending the right message, to the right object, with the right data (arguments). And I am just in need of creating a very simple object that has no logic, just the responsibility of knowing how ActionMailer wants to be called.
I prefer to do this because I prefer to have more control over the dependencies I have. This is form me an example of the "Dependency inversion principle".
I am not sure if it is your taste, but is another way to solve the problem =).
참고URL : https://stackoverflow.com/questions/27647749/how-to-test-actionmailer-deliver-later-with-rspec
'Program Tip' 카테고리의 다른 글
AngularJS 용 ui-router로 쿼리 매개 변수를 추출하는 방법은 무엇입니까? (0) | 2020.11.29 |
---|---|
Objective-C를 사용하여 다중 파트 / 양식 데이터 POST (0) | 2020.11.29 |
GUID는 정확히 무엇입니까? (0) | 2020.11.29 |
Java의 void 메소드에서 return 키워드는 무엇을합니까? (0) | 2020.11.29 |
jQuery.validation에서 같지 않음 규칙을 추가하는 방법 (0) | 2020.11.29 |