102

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

我想,也许,将电子邮件存储在本地的文件夹中,因为它们被发送。关于如何实现它的任何提示?

4

10 回答 10

200

Django 测试框架有一些内置的帮助器来帮助您测试电子邮件服务

来自文档的示例(短版):

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.',
            'from@example.com', ['to@example.com'],
            fail_silently=False)
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].subject, 'Subject here')
于 2010-09-16T16:18:09.447 回答
47

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

于 2010-09-16T16:10:54.013 回答
25

如果您要进行单元测试,最好的解决方案是使用django 提供的In-memory 后端。

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.', 'from@example.com', ['to@example.com'])
    assert len(mail.outbox) == 1

def test_send_again(self):
    mail.send_mail('subject', 'body.', 'from@example.com', ['to@example.com'])
    assert len(mail.outbox) == 1
于 2016-04-25T17:33:56.183 回答
10

使用MailHog

受 MailCatcher 启发,更易于安装。

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


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

吉姆能做什么?

  • 拒绝连接
  • 速率限制连接
  • 拒绝认证
  • 拒绝发件人
  • 拒绝收件人

在此处阅读更多相关信息。


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

于 2016-08-26T13:21:17.673 回答
5

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

于 2010-09-16T16:17:46.033 回答
4

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

于 2014-06-26T18:29:52.187 回答
2

为测试目的修补 SMTPLib 有助于在不发送邮件的情况下测试发送邮件。

于 2010-09-16T16:15:03.643 回答
1

为什么不通过继承smtpd.SMTPServer和启动您自己的非常简单的 SMTP 服务器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()

每当您的 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()来结束异步循环(停止服务器监听)。

于 2016-09-18T13:54:18.503 回答
1

将这里的一些部分捆绑在一起,这是一个基于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 %}

网址

path("mailcheck/", view=mailcheck, name="mailcheck"),
于 2019-05-18T05:13:52.797 回答
0

如果您有可用的 TomCat 服务器或其他 servlet 引擎,那么一个不错的方法是“Post Hoc”,它是一个小型服务器,看起来与 SMTP 服务器完全一样,但它包含一个用户界面,允许您查看和检查已发送的电子邮件。它是开源的并且免费提供。

在以下位置找到它:Post Hoc GitHub 站点

请参阅博客文章: PostHoc:测试发送电子邮件的应用程序

于 2017-07-24T21:41:36.740 回答