Comment tester ActionMailer deliver_later avec rspec
tentative de mise à niveau pour les Rails 4.2, à l'aide de delayed_job_active_record. Je n'ai pas mis le delayed_job backend pour l'environnement de test de la pensée de cette façon, les emplois d'exécuter tout de suite.
Je suis en train de tester le nouveau "deliver_later' méthode avec Rspec, mais je ne suis pas sûr de savoir comment.
Ancien contrôleur de code:
ServiceMailer.delay.new_user(@user)
Nouveau code de contrôleur:
ServiceMailer.new_user(@user).deliver_later
J'ai UTILISÉ pour tester de la sorte:
expect(ServiceMailer).to receive(:new_user).with(@user).and_return(double("mailer", :deliver => true))
Maintenant j'ai des erreurs à l'aide que. (Double "mailer" a reçu le message inattendu :deliver_later avec (pas args))
Juste
expect(ServiceMailer).to receive(:new_user)
ne parvient pas trop à 'undefined method `deliver_later' for nil:NilClass'
J'ai essayé quelques-uns des exemples qui vous permettent de voir si les emplois sont mis en file d'attente à l'aide de test_helper dans ActiveJob mais je n'ai pas réussi à tester que la tâche est en attente.
expect(enqueued_jobs.size).to eq(1)
Ce passe si le test_helper est inclus, mais il ne me permet pas de le vérifier est le bon e-mail qui est envoyé.
Ce que je veux faire est:
- test de l'e-mail correcte est mis en file d'attente (ou exécuté tout de suite dans l'essai env)
- avec les paramètres corrects (@user)
Des idées??
grâce
Vous devez vous connecter pour publier un commentaire.
Si je vous comprends bien, vous pourriez faire:
Le point clé est que vous devez en quelque sorte fournir un double de
deliver_later
.allow(message_delivery).to …
? Après tout, vous avez déjà tester le résultat en attendantnew_user
.deliver_later
méthode est supprimé à partir du contrôleur, en utilisantallow
nous ne serons pas en mesure de rattraper ce que le droit? Je veux dire que le test sera toujours. Pensez-vous encore à l'aide d'unallow
va être une meilleure idée que d'utiliserexpect
? Je ne vois queexpect
drapeaux si ledeliver_later
a été supprimé par erreur, & que c'est en fait pourquoi je voulais parler de ça en général. Il serait génial si vous pouviez donner plus de détails sur les raisons deallow
est mieux avec le contexte ci-dessus.ServiceMailer
'snew_user
méthode est appelée. Vous êtes libre de créer un autre test qui teste pour ladeliver_later
appel d'une méthode, une fois que le courrier a été créé.allow
en fonction du contexte de testServiceMailer's new_user
méthode. Dans le cas où j'aurais à testerdeliver_later
, je pensais que j'avais juste ajouter une autre assertion pour le test existant(qui vérifieServiceMailer's new_user
méthode) pour vérifier quelque chose commeexpect(mailer_object).to receive(:deliver_later)
au lieu de tester ce qu'un autre test en tout. Il serait intéressant de savoir pourquoi vous préférez un test séparé pour cela, dans le cas où nous n'aurions jamais testdeliver_later
.allow
a ses propres avantages et inconvénients et il dépend de ce que vous êtes cherchant d'abord à couvrir dans le cadre de votre essai et quel type de flexibilité que vous voulez, quand vous cherchez en termes de votre code. Merci pour cette conversation et pour avoir répondu à mes questions 🙂Si vous trouvez cette question, mais utilisez ActiveJob plutôt que de simplement DelayedJob sur son propre, et sont à l'aide de Rails 5, je vous recommande la configuration de ActionMailer dans
config/environments/test.rb
:(c'était le comportement par défaut avant de Rails 5)
À l'aide de ActiveJob et rspec 3.4+ vous pouvez utiliser
have_enqueued_job
comme ceci:Je vais ajouter ma réponse, car aucun des autres était assez bon pour moi:
1) Il n'est pas nécessaire de se moquer de l'Expéditeur: les Rails se fait déjà pour vous.
2) Il n'y a pas vraiment besoin de déclencher la création de l'e-mail: cela consomme du temps et de ralentir votre test!
C'est pourquoi, dans
environments/test.rb
vous devez avoir les options suivantes set:De nouveau: ne livrez pas vos e-mails à l'aide de
deliver_now
mais toujours utilisationdeliver_later
. Qui empêche les utilisateurs d'attente pour l'efficacité de la livraison de l'e-mail. Si vous n'avez passidekiq
,sucker_punch
, ou de toute autre production, il suffit d'utiliserconfig.active_job.queue_adapter = :async
. Et soitasync
ouinline
pour le développement de l'environnement.Compte tenu de la configuration suivante pour l'environnement de test, vous e-mails seront toujours mis en file d'attente et jamais exécuté pour la livraison: cela empêche de se moquant d'eux et vous pouvez vérifier qu'ils sont correctement mis en file d'attente.
En vous tests, toujours diviser le test en deux:
1) Une unité de test pour vérifier que le mail est mis en file d'attente correctement et avec les paramètres corrects
2) Un test unitaire pour le mail pour vérifier que le sujet, l'expéditeur, le destinataire et le contenu sont corrects.
Donné le scénario suivant:
Écrire un test pour vérifier l'e-mail est correctement mis en file d'attente:
et écrire un essai séparé pour votre e-mail
Lorsque vous écrivez un système de test, n'hésitez pas à décider si vous voulez vraiment envoyer de mails là, car la vitesse n'est pas question que beaucoup plus. Personnellement, j'aime à configurer les éléments suivants:
et affecter la
:mailer
attribut pour les tests ont été fait je veux envoyer des e-mails.Pour en savoir plus sur la façon de configurer correctement votre e-mail dans les Rails de lire cet article: https://medium.com/@coorasse/the-correct-emails-configuration-in-rails-c1d8418c0bfd
ActionMailer::MailDeliveryJob
au lieu deActionMailer::DeliveryJob
Ajouter ceci:
Référence: http://mrlab.sk/testing-email-delivery-with-deliver-later.html
deliver_later(wait: 2.minutes)
. Donc je n'aideliver_later(options={})
NameError: uninitialized constant ActionMailer
Une meilleure solution (que monkeypatching
deliver_later
) est:La
around { |example| perform_enqueued_jobs(&example) }
s'assure que les tâches en arrière-plan sont exécutés avant de vérifier les valeurs de test.Je suis venu avec le même doute et résolus en moins verbeux (ligne unique) moyen inspiré par cette réponse
Noter que la dernière
with(no_args)
est essentiel.Mais, si vous ne vous souciez pas si
deliver_later
est appelé, il suffit de faire:expect(ServiceMailer).to expect(:new_user).with(@user).and_call_original
Un moyen simple est:
Je suis venu ici à la recherche d'une réponse pour un test complet, donc, pas juste demander si il y a un mail en attente d'être envoyés, en outre, pour son destinataire, objet, etc...
J'ai une solution, que vient de ici, mais avec un peu de changement:
Comme il le dit, le curial partie est
Le problème est que les paramètres que l'expéditeur reçoit, dans ce cas, est différent des paramètres que reçoit de la production, de la production, si le premier paramètre est un Modèle, maintenant dans les tests recevrez un hachage, il en sera de crash
Donc, si on peut appeler l'expéditeur comme
UserMailer.welcome_email(@user).deliver_later
l'expéditeur reçoit dans la production d'un Utilisateur, mais dans les tests recevrez{"_aj_globalid"=>"gid://forjartistica/User/1"}
Tous les commentaires seront apprécier,
Le moins douloureux solution que j'ai trouvé est de changer la manière que j'appelle les expéditeurs, en passant, le modèle de l'identifiant et du pas le modèle:
UserMailer.welcome_email(@user.id).deliver_later
Cette réponse est un peu différent, mais il peut aider dans les cas comme un nouveau changement dans les rails de l'API, ou un changement dans la façon dont vous souhaitez vous faire livrer (comme l'utilisation
deliver_now
au lieu dedeliver_later
).Ce que je fais la plupart du temps est de passer un mailer comme une dépendance à la méthode que je suis en train de tester, mais je ne passe pas un mailer de rails, j'ai plutôt passer un objet qui va faire les choses dans la "voie que je veux"...
Par exemple si je veux vérifier que je suis d'envoyer le bon mail après l'inscription d'un utilisateur... je pourrais le faire...
Et puis, dans le contrôleur, où j'ai appeler cette méthode, je voudrais écrire le "réel" de la mise en œuvre de cette mailer...
De cette façon, je sens que je suis en train de tester que je suis d'envoyer le bon message, à la droite de l'objet, avec les bonnes données (arguments). Et je suis dans le besoin de la création d'un objet très simple qui n'a pas de logique, juste la responsabilité de savoir comment ActionMailer veut être appelé.
Je préfère le faire parce que je préfère avoir plus de contrôle sur les dépendances que j'ai. Il s'agit du formulaire-moi un exemple de la "Inversion de la dépendance principe".
Je ne suis pas sûr si c'est à votre goût, mais il est une autre façon de résoudre le problème =).
Cette réponse est pour les Rails de Test, pas pour rspec...
Si vous utilisez
delivery_later
comme ceci:Vous pouvez vérifier dans votre test si le mail a été ajouté à la file d'attente:
Si vous faites cela, vous serez surpris par l'échec de test qui vous dit assert_enqueued_jobs n'est pas défini pour nous.
C'est parce que notre test hérite de ActionController::cas de test qui, au moment de la rédaction, ne comprend pas ActiveJob::TestHelper.
Mais on peut rapidement résoudre ce problème:
De référence:
https://www.engineyard.com/blog/testing-async-emails-rails-42
Pour les dernières Googlers: