C# 无效功能的NUnit测试(发送电子邮件)

C# 无效功能的NUnit测试(发送电子邮件),c#,asp.net,unit-testing,nunit,C#,Asp.net,Unit Testing,Nunit,我有一个发送电子邮件的void函数。我需要为此编写测试​​功能。如何做到这一点 public void SendAdminMail(string subject, string body, string adminAddress) { var email = Email. From(ConfigurationManager.AppSettings["Mail.NoReply.Address"].ToString(CultureInfo.Invar

我有一个发送电子邮件的void函数。我需要为此编写测试​​功能。如何做到这一点

public void SendAdminMail(string subject, string body, string adminAddress)
    {

        var email = Email.
            From(ConfigurationManager.AppSettings["Mail.NoReply.Address"].ToString(CultureInfo.InvariantCulture)).
            To(adminAddress).
            Subject(subject).
            Body(body).
            UsingClient(GetOfficeClient());
        email.Message.SubjectEncoding = Encoding.UTF8;
        email.Message.BodyEncoding = Encoding.UTF8;

        email.Send();
    }

如果您碰巧有Visual Studio 2012 Ultimate或Premium,那么您可以为
System.Net.Mail
程序集生成伪造程序集并使用它

有关假货的更多信息,请访问


如果您没有VS2012 Ultimate或Premium,那么我的答案完全是usless:)

在这种情况下,这将非常困难-除非您能够通过反射以某种方式切换
电子邮件(正如有趣的指出,现在可能没有那么困难;))

典型的解决方案是为类提供接口,例如
interface-EmailFactory
。那么你就有:

private EmailFactory emailFactory;
public void SendAdminMail(string subject, string body, string adminAddress)
{
    var email = emailFactory
        .From(ConfigurationManager
                  .AppSettings["Mail.NoReply.Address"]
                  .ToString(CultureInfo.InvariantCulture))
        .To(adminAddress)
        .Subject(subject)
        .Body(body)
        .UsingClient(GetOfficeClient());

    email.Message.SubjectEncoding = Encoding.UTF8;
    email.Message.BodyEncoding = Encoding.UTF8;

    email.Send();
}

然后你可以向你的类提供这个工厂的存根,这将创建电子邮件模拟,你可以在上面验证正确的行为。

我开始认为,当网络电缆拔下时,单元测试应该可以工作。

你到底想测试什么?

您可以使用mock或false来验证是否按预期调用了方法。
如果能够将这个类去掉,并在其他地方使用这个存根,以确保其余的测试不会在每次运行时都发送电子邮件,这可能会更有用

如何做到这一点

除非您使用某些库或框架来做大量变通,否则测试此方法并不简单,因为您的方法违反了SRP原则

尽管您的方法称为SendAmdinMail,但实际上只有一条语句处理发送,即
email.Send()
,该方法的其余部分是使用.NET API构建邮件消息

您应该将此方法分解为不同的部分,如CreateMailBody、CreateHeader、LoadMailConfig和将邮件发送隐藏在抽象(接口、抽象类)后面,以便创建邮件发送服务的模拟


这样,您还可以测试邮件正文的内容,您可以测试收件人等。

您想要编写的是一个而不是一个

如果您想测试您的代码是否调用了一些负责发送电子邮件的函数,那么您需要通过使用一些模拟框架(例如)编写一个单元测试

[测试]
public void ShouldSendEmail()
{
IEmailService mockEmailService=Substitute.For();
MyEmailSendingThingUnderTest=新的MyEmailSendingThingy(mockEmailService);
thingUnderTest.dosomework涉及到邮件();
mockEmailService.Received().SendEmail(“foo@foo.foo", "bar@bar.bar“,”主题“,”一些文本“);
}
如果您想测试您的应用程序是否确实发送了一封电子邮件,那么您需要编写一个集成测试。有很多方法可以实现这一点

  • 编写一个测试,等待一段时间,然后从测试电子邮件帐户检索电子邮件,并验证它是否收到了预期的邮件
  • 将电子邮件发送到内部SMTP服务器,该服务器设置为将电子邮件存储在drop文件夹中,然后测试代码可以检查该文件夹
  • 模拟SMTP端点,并模拟调用SMTP服务器时所期望的响应。然后,您可以使用端点验证它是否收到了预期的电子邮件
  • 模拟系统。使用或伪造邮件组件
  • 1和2需要大量的工作,并且您必须考虑发送电子邮件的异步性质。这意味着你将不得不休眠你的程序或使用某种触发器来等待电子邮件到达目的地,这应该尽可能避免

    3是我个人的选择,原因有三。您可以在测试过程中托管它,从而更容易进行断言。其次,它是可重用的,可以在任何测试框架中使用。第三,作为奖励,你可以更好地理解电子邮件的工作原理

    就我个人而言,我不喜欢模仿静态或混凝土组件。。它会导致坏习惯和坏代码。然而,正确使用的工具可能是有用的,这可能是测试代码这一部分的最佳方法


    最后确保您测试的是正确的东西。您不需要测试Microsoft是否编写了系统。正确发送电子邮件组件,您只需要确保正确调用组件。

    您看到了吗:您要做的是所谓的模拟。。。99%的时间都与依赖注入有关。。。如果没有一个或另一个,做你想做的事情是非常复杂的。自从本次更新以来,仿冒品也包括在VS2012 Premium中:谢谢,我不知道这一点。
    [Test]
    public void ShouldSendEmail()
    {
        IEmailService mockEmailService = Substitute.For<IEmailService>();
        MyEmailSendingThingy thingUnderTest = new MyEmailSendingThingy(mockEmailService);
    
        thingUnderTest.DoSomeWorkThatInvolvesSendingEmails();
    
        mockEmailService.Received().SendEmail("foo@foo.foo", "bar@bar.bar", "Subject", "Some Text");
    }