在 Django 中测试电子邮件发送

发布于 2024-09-19 22:49:36 字数 203 浏览 9 评论 0原文

我需要测试我的 Django 应用程序发送的电子邮件是否包含正确的内容。我不想依赖外部系统(例如临时 gmail 帐户),因为我我没有测试实际的电子邮件服务...

我想,也许,将电子邮件存储在本地的文件夹中,因为它们被发送。 关于如何实现它有什么建议吗?

I need to test that my Django application sends e-mails with correct content. I don't want to rely on external systems (like an ad-hoc gmail account), since I'm not testing the actual e-mail service...

I would like to, maybe, store the emails locally, within a folder as they are sent.
Any tip on how to achieve it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

年华零落成诗 2024-09-26 22:49:36

Django 测试框架有一些内置的帮助程序来帮助您进行测试 e -邮件服务

文档示例(简短版本):

from django.core import mail
from django.test import TestCase

class EmailTest(TestCase):
    def test_send_email(self):
        mail.send_mail('Subject here', 'Here is the message.',
            '[email protected]', ['[email protected]'],
            fail_silently=False)
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].subject, 'Subject here')

Django test framework has some built in helpers to aid you with testing e-mail service.

Example from docs (short version):

from django.core import mail
from django.test import TestCase

class EmailTest(TestCase):
    def test_send_email(self):
        mail.send_mail('Subject here', 'Here is the message.',
            '[email protected]', ['[email protected]'],
            fail_silently=False)
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].subject, 'Subject here')
毁梦 2024-09-26 22:49:36

您可以使用文件后端发送电子邮件,这是一个非常方便的开发和测试解决方案;电子邮件不会发送,而是存储在您可以指定的文件夹中!

You can use a file backend for sending emails which is a very handy solution for development and testing; emails are not sent but stored in a folder you can specify!

许一世地老天荒 2024-09-26 22:49:36

如果您正在进行单元测试,最好的解决方案是使用 django 提供的内存后端

EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'

以将其用作 py.test 固定装置为例

@pytest.fixture(autouse=True)
def email_backend_setup(self, settings):
    settings.EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'  

在每次测试中,mail.outbox 都会随服务器重置,因此测试之间不会产生副作用。

from django.core import mail

def test_send(self):
    mail.send_mail('subject', 'body.', '[email protected]', ['[email protected]'])
    assert len(mail.outbox) == 1

def test_send_again(self):
    mail.send_mail('subject', 'body.', '[email protected]', ['[email protected]'])
    assert len(mail.outbox) == 1

If you are into unit-testing the best solution is to use the In-memory backend provided by django.

EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'

Take the case of use it as a py.test fixture

@pytest.fixture(autouse=True)
def email_backend_setup(self, settings):
    settings.EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'  

In each test, the mail.outbox is reset with the server, so there are no side effects between tests.

from django.core import mail

def test_send(self):
    mail.send_mail('subject', 'body.', '[email protected]', ['[email protected]'])
    assert len(mail.outbox) == 1

def test_send_again(self):
    mail.send_mail('subject', 'body.', '[email protected]', ['[email protected]'])
    assert len(mail.outbox) == 1
朕就是辣么酷 2024-09-26 22:49:36

2024 年更新: 遗憾的是,MailHog 自 2020 年以来似乎就不再维护了。我看到 axllent/mailpit 到处都有推荐,但我自己还没用过。


使用 MailHog

受 MailCatcher 启发,更易于安装。

使用 Go 构建 - MailHog 无需安装即可在多个平台上运行。

此外,它还有一个名为 Jim 的组件,即 MailHog Chaos Monkey,它使您能够测试发送电子邮件时发生的各种问题:

吉姆能做什么?

  • 拒绝连接
  • 限制连接速率
  • 拒绝身份验证
  • 拒绝发件人
  • 拒绝收件人

了解更多相关信息 此处

(与原始的 mailcatcher 不同,在发送带有表情符号(以 UTF-8 编码)的电子邮件时失败 并且它在当前版本中并未真正修复,MailHog 可以正常工作。)

2024 update: MailHog sadly seems to be unmaintained since 2020. I saw axllent/mailpit being recommended here and there, but I haven't used it myself yet.


Use MailHog

Inspired by MailCatcher, easier to install.

Built with Go - MailHog runs without installation on multiple platforms.

Also, it has a component called Jim, the MailHog Chaos Monkey, which enables you to test sending emails with various problems happening:

What can Jim do?

  • Reject connections
  • Rate limit connections
  • Reject authentication
  • Reject senders
  • Reject recipients

Read more about it here.

(Unlike original mailcatcher, which failed on me when sending emails with emoji, encoded in UTF-8 and it WASN'T really fixed in the current release, MailHog just works.)

苯莒 2024-09-26 22:49:36

对于任何不需要发送附件的项目,我使用 django-mailer,它具有所有出站电子邮件的好处都是在队列中结束,直到我触发它们的发送,即使在发送之后,它们也会被记录 - 所有这些都在管理中可见,从而可以轻松快速检查您的电子邮件代码是什么试图向内管开火。

For any project that doesn't require sending attachments, I use django-mailer, which has the benefit of all outbound emails ending up in a queue until I trigger their sending, and even after they've been sent, they are then logged - all of which is visible in the Admin, making it easy to quickly check what you emailing code is trying to fire off into the intertubes.

留一抹残留的笑 2024-09-26 22:49:36

Django 还有一个内存电子邮件后端。更多详细信息,请参阅内存后端下的文档。这存在于 Django 1.6 中,不确定它是否存在于更早的版本中。

Django also has an in-memory email backend. More details in the docs under In-memory backend. This is present in Django 1.6 not sure if it's present in anything earlier.

可爱暴击 2024-09-26 22:49:36

出于测试目的修补 SMTPLib 可以帮助测试发送邮件而不发送邮件。

Patching SMTPLib for testing purposes can help test sending mails without sending them.

[浮城] 2024-09-26 22:49:36

为什么不通过继承 smtpd.SMTPServerthreading.Thread< /a>:

class TestingSMTPServer(smtpd.SMTPServer, threading.Thread):
    def __init__(self, port=25):
        smtpd.SMTPServer.__init__(
            self,
            ('localhost', port),
            ('localhost', port),
            decode_data=False
        )
        threading.Thread.__init__(self)

    def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
        self.received_peer = peer
        self.received_mailfrom = mailfrom
        self.received_rcpttos = rcpttos
        self.received_data = data

    def run(self):
        asyncore.loop()

每当您的 SMTP 服务器收到邮件请求时就会调用 process_message,您可以在那里做任何您想做的事情。

在测试代​​码中,执行如下操作:

smtp_server = TestingSMTPServer()
smtp_server.start()
do_thing_that_would_send_a_mail()
smtp_server.close()
self.assertIn(b'hello', smtp_server.received_data)

只需记住 close() asyncore .dispatcher 通过调用 smtp_server.close() 来结束 asyncore 循环(停止服务器监听)。

Why not start your own really simple SMTP Server by inherit from smtpd.SMTPServer and threading.Thread:

class TestingSMTPServer(smtpd.SMTPServer, threading.Thread):
    def __init__(self, port=25):
        smtpd.SMTPServer.__init__(
            self,
            ('localhost', port),
            ('localhost', port),
            decode_data=False
        )
        threading.Thread.__init__(self)

    def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
        self.received_peer = peer
        self.received_mailfrom = mailfrom
        self.received_rcpttos = rcpttos
        self.received_data = data

    def run(self):
        asyncore.loop()

process_message is called whenever your SMTP Server receive a mail request, you can do whatever you want there.

In the testing code, do something like this:

smtp_server = TestingSMTPServer()
smtp_server.start()
do_thing_that_would_send_a_mail()
smtp_server.close()
self.assertIn(b'hello', smtp_server.received_data)

Just remember to close() the asyncore.dispatcher by calling smtp_server.close() to end the asyncore loop(stop the server from listening).

坠似风落 2024-09-26 22:49:36

将这里的一些部分结合在一起,这是一个基于 filebased.EmailBackend 的简单设置。这将呈现一个链接到各个日志文件的列表视图,这些日志文件具有方便的时间戳文件名。单击列表中的链接会在浏览器中显示该消息(原始):

设置

EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
EMAIL_FILE_PATH = f"{MEDIA_ROOT}/email_out"

查看

import os

from django.conf import settings
from django.shortcuts import render

def mailcheck(request):

    path = f"{settings.MEDIA_ROOT}/email_out"
    mail_list = os.listdir(path)

    return render(request, "mailcheck.html", context={"mail_list": mail_list})

模板

{% if mail_list %}
  <ul>
  {% for msg in mail_list %}
    <li>
      <a href="{{ MEDIA_URL }}email_out/{{msg}}">{{ msg }}</a>
    </li>
  {% endfor %}
  </ul>
{% else %}
  No messages found.
{% endif %}

url

path("mailcheck/", view=mailcheck, name="mailcheck"),

Tying a few of the pieces here together, here's a straightforward setup based on filebased.EmailBackend. This renders a list view linking to the individual log files, which have conveniently timestamped filenames. Clicking a link in the list displays that message in the browser (raw):

Settings

EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
EMAIL_FILE_PATH = f"{MEDIA_ROOT}/email_out"

View

import os

from django.conf import settings
from django.shortcuts import render

def mailcheck(request):

    path = f"{settings.MEDIA_ROOT}/email_out"
    mail_list = os.listdir(path)

    return render(request, "mailcheck.html", context={"mail_list": mail_list})

Template

{% if mail_list %}
  <ul>
  {% for msg in mail_list %}
    <li>
      <a href="{{ MEDIA_URL }}email_out/{{msg}}">{{ msg }}</a>
    </li>
  {% endfor %}
  </ul>
{% else %}
  No messages found.
{% endif %}

urls

path("mailcheck/", view=mailcheck, name="mailcheck"),
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文