【问题标题】:How to unit test email sending?如何对电子邮件发送进行单元测试?
【发布时间】:2011-07-12 07:10:06
【问题描述】:

我想使用 .NET (C#) 框架或任何兼容库测试我的电子邮件发送功能,有什么建议吗?

【问题讨论】:

标签: c# .net unit-testing


【解决方案1】:

如果你只需要测试发送电子邮件,你可以像这样配置你的 .config 文件

<system.net>
    <mailSettings>
        <smtp deliveryMethod="SpecifiedPickupDirectory">
            <specifiedPickupDirectory pickupDirectoryLocation="C:\TempMail" />
        </smtp>
    </mailSettings>
</system.net>

使用这些设置,您的消息将不会通过网络发送,而是作为扩展名为 .eml 的物理文件丢弃在您在 pickupDirectoryLocation 属性中配置的文件夹中。您可以借助 System.IO 命名空间中的类来检查它们。

MSDN 文档是here

【讨论】:

  • 如果在配置中有 msdn 链接会很棒。
【解决方案2】:

我不同意在单元测试期间实际发送电子邮件的想法。这给 IMO 带来了很多麻烦,并且违背了单元测试的本意。

在我的应用程序中,我有一个 IMailManager 接口,其中包含 SendPasswordResetEmail(string emailAddress) 等方法。我在单元测试期间模拟了这个对象,并确保我的组件正在调用正确的邮件管理器方法。

我对@9​​87654323@ 的实际生产实现通常在内部使用System.Net.Mail.SmtpClient,您不需要对其进行测试。让微软测试一下。您需要做的就是确保您的 smtp 设置在部署时正确设置,这不应该是单元测试的关注点。

如果您需要测试您的邮件组件本身,即确保它生成正确的消息正文等,我建议您模拟出将该功能隔离到一个独立的单元测试中所需的内容。

【讨论】:

  • 您如何知道您的 SendPasswordResetEmail 方法是否正确实施?你没有。这就是为什么你需要测试它。是的,我可能不会将这些称为单元测试,但无论如何都需要对其进行测试。
  • @SleeperSmith 这不是单元测试,而是集成测试,因为它与外部系统通信。这种测试是必需的,但不是单元测试能力。
  • 问题是测试实际的“电子邮件发送功能”。我很确定“单元测试”作者确实想到了一种集成测试。
【解决方案3】:

您不需要测试实际的电子邮件发送功能;这是 .NET 框架的一部分,并且已经过测试。

您需要进行单元测试的是电子邮件创建业务逻辑。将其封装在这样的服务中:

public interface IPasswordResetEmailCreator
{
    MailMessage Create(string emailAddress);
}

并实施它。然后为这个实现编写一个单元测试,并验证它返回的MailMessage是否符合你的要求。

使用SpecsFor 框架的此单元测试的示例实现:

public class PasswordResetEmailCreatorSpecs
{
    public class given_a_registered_user : SpecsFor<PasswordResetEmailCreator>
    {
        private string _emailAddress;
        private MailMessage _email;

        protected override void Given()
        {
            _emailAddress = "user@example.org";
        }

        protected override void When()
        {
            _email = SUT.Create(_emailAddress);
        }

        [Test]
        public void then_the_body_must_contain_the_reset_uri()
        {
            _email.Body.ShouldContain("/Password/Reset/");
        }

        [Test]
        public void then_the_email_must_be_for_the_user()
        {
            _email.To[0].Address.ShouldEqual(_emailAddress);
        }

        [Test]
        public void then_the_subject_must_be_the_expected()
        {
            _email.Subject.ShouldEqual("Your email reset link");
        }
    }
}

【讨论】:

    【解决方案4】:

    在approvaltests(www.approvaltests.com 或nuget)中有一个非常简单的方法来测试邮件内容。代码很简单:

    EmailApprovals.Verify(mail);
    

    这将创建 .eml 文件,并允许您在 Outlook 中查看结果。此外,一旦您批准结果(将文件重命名为 .approved),测试将通过而无需打开 Outlook。 (这与@whyleee 提到的过程非常相似,但不使用配置文件。)

    I have published a short video on the process on YouTube.

    【讨论】:

      【解决方案5】:

      我通常有一个“OverrideEmailAddress”设置,我将其设置为我自己的电子邮件,然后您运行的任何电子邮件测试都不会发送给您的客户或他们最初发送给的任何人。我有一个帮助方法,我通过它发送所有电子邮件,如果存在,此方法将使用该设置。或者,您可以在电子邮件底部添加原始收件人。

      如果您需要确认电子邮件是否已收到,您必须编写一些代码来实际检查电子邮件地址,然后确认消息是否正确。

      不确定这是不是你的意思。

      【讨论】:

        【解决方案6】:

        我可能会在 .NET SmtpClient 类之上编写一个非常薄的抽象层。然后我可以用一个模型类代替单元测试。当然,你不能对包装器进行单元测试,但无论如何它应该是微不足道的,几乎不会改变。

        【讨论】:

          【解决方案7】:

          如果您想测试它,我建议您查看 mail4net:www.mail4net.com

          它是一个商业产品,但它允许您使用伪造的 SMTP 服务发送电子邮件,然后查询伪造的。

          // Create Mail4Net FakeClient.
          var client = new Mail4Net.Client.FakeClient();
          
          // Send email.
          client.Send(from, to, subject, body);
          

          然后,您可以查询客户端:

          // Count the number of emails sent.
          var count = client.Count();
          
          // Get first email.
          var message = client[0];
          

          【讨论】:

            【解决方案8】:

            我的快速而肮脏的方法与top answer by whylee 非常相似,只是它不需要接触任何配置文件(因为我可能需要针对不同的单元测试进行不同的设置)

            在您的电子邮件发送代码中

            if (ConfigurationManager.AppSettings["smtpDir"] == null)
                smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
            else
            {
                smtp.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
                smtp.PickupDirectoryLocation = ConfigurationManager.AppSettings["smtpDir"];
            }
            

            在您的单元测试中

            ConfigurationManager.AppSettings["smtpDirectory"] = dir;
            
            //do some testing then change it back
            
            ConfigurationManager.AppSettings["smtpDirectory"] = null;
            

            PS。我知道所有的缺点,但以防万一有人需要这个......

            【讨论】:

              【解决方案9】:

              我用于集成测试的一个快速而肮脏的解决方案是简单地实现一个电子邮件管理器,将电子邮件存储在内存中 - 然后您可以在测试断言中访问它 - 而不是实际发送它。目前每个测试发送的最大电子邮件数量是两封,所以我想这就是为什么在这种情况下就足够了。

              当然,您必须在每次测试后清理内存中已发送电子邮件的列表。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2011-09-16
                • 1970-01-01
                • 2019-01-29
                • 1970-01-01
                • 2014-11-04
                • 2011-03-07
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多