попытка обновить до направляющих 4.2, использование delayed_job_active_record. Я не установил delayed_job бэкенд для тестовой среды, как думается тот способ, которым задания немедленно выполнились бы.
Я пытаюсь протестировать новый 'deliver_later' метод с Rspec, но я не уверен как.
Старый код контроллера:
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 с (никакой args))
Просто
expect(ServiceMailer).to receive(:new_user)
сбои также с 'неопределенным методом 'deliver_later' для nil:NilClass'
Я попробовал некоторые примеры, которые позволяют Вам видеть, ставятся ли задания в очередь с помощью test_helper в ActiveJob, но мне не удалось протестировать это, корректное задание ставится в очередь.
expect(enqueued_jobs.size).to eq(1)
Это передает, если test_helper включен, но он не позволяет мне проверять, что это - корректное электронное письмо, которое посылается.
То, что я хочу сделать:
Какие-либо идеи??спасибо
Если я понимаю Вас правильно, Вы могли бы сделать:
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
.
Если Вы находите этот вопрос, но используете ActiveJob, а не просто DelayedJob самостоятельно, и используете направляющие 5, я рекомендую настроить ActionMailer в config/environments/test.rb
:
config.active_job.queue_adapter = :inline
(это было поведением по умолчанию до направляющих 5)
Используя ActiveJob и rspec 3.4 + Вы могли использовать have_enqueued_job
как это:
expect {
YourMailer.your_method.deliver_later
}.to have_enqueued_job.on_queue('mailers')
Я добавлю свой ответ, потому что ни один из других не был достаточно хорош для меня:
1) нет никакой потребности дразнить Почтовую программу: Направляющие в основном уже делают это для Вас.
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
, атрибут к тестам был, я хочу на самом деле послать электронные письма.
Для больше о том, как правильно настроить Вашу электронную почту в направляющих, читает эту статью: 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
Я приехал, сюда ища ответ для полного тестирования, таким образом, не всего выяснение, если существует одна почта, ожидающая, чтобы быть отправленной, кроме того, для ее получателя, подвергнуть... и т.д.
, у меня есть решение, чем прибывает от сюда , но с небольшим изменением:
, Как это говорит, curial часть
mail = perform_enqueued_jobs { ActionMailer::DeliveryJob.perform_now(*enqueued_jobs.first[:args]) }
, проблема состоит в том, что параметры, чем почтовая программа получают, в этом случае, отличается от параметров, чем получает в производстве, в производстве, если первым параметром будет Модель, то теперь в тестировании получит хеш, так откажет
enqueued_jobs.first[:args]
["UserMailer", "welcome_email", "deliver_now", {"_aj_globalid"=>"gid://forjartistica/User/1"}]
Так, если мы назовем почтовую программу как UserMailer.welcome_email(@user).deliver_later
, почтовая программа получает в производстве, которое Пользователь, но в тестировании получит {"_aj_globalid"=>"gid://forjartistica/User/1"}
, Все комментарии будут, ценят, менее болезненное решение, которое я нашел, изменяет способ, которым я называю почтовые программы, передачу, идентификатор модели а не модель:
UserMailer.welcome_email(@user.id).deliver_later
Этот ответ немного отличается, но может помочь в случаях как новое изменение в направляющих 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 хочет быть названным.
я предпочитаю делать это, потому что я предпочитаю иметь больше контроля над зависимостями, которые я имею. Это, формируют меня пример "Принцип инверсии зависимости" .
я не уверен - ли это Ваш вкус, но является другим способом решить проблему =).