【问题标题】:how to mock the SmtpClient object which is used inside a function for Unit Testing如何模拟用于单元测试的函数内部的 SmtpClient 对象
【发布时间】:2017-11-16 03:46:43
【问题描述】:

我想为存在于 BatchProcess 中的 SendMail 方法编写 Nunit 或单元测试,而不发送邮件。

如何模拟存在于另一个方法中的 SmtpClient。请帮忙。

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            //Assuming we are populating the emails from the data from database
            List<EmailEntity> emails = new List<EmailEntity>();
            BatchProcess.SendMail(emails);
        }
    }

    public class EmailEntity
    {
        public string ToAddress { get; set; }
        public string Subject { get; set; }
        public string Body { get; set; }
    }

    public class BatchProcess
    {
        public static void SendMail(List<EmailEntity> emails)
        {
            foreach (EmailEntity email in emails)
            {
                MailMessage mail = new MailMessage();
                SmtpClient SmtpServer = new SmtpClient("sampleSmtp.sampleTest.com");
                mail.From = new MailAddress("your_email_address@gmail.com");
                mail.To.Add(email.ToAddress);
                mail.Subject = email.Subject;
                mail.Body = email.Body;
                SmtpServer.Port = 587;
                SmtpServer.Credentials = new System.Net.NetworkCredential("username", "password");
                SmtpServer.EnableSsl = true;
                SmtpServer.Send(mail);
            }
        }
    }
}

【问题讨论】:

  • 你可以把它放在接口 ISmtpClient 后面。
  • 您的代码与实现问题紧密耦合,因此很难单独进行测试。应该有一个抽象层,可以提供更大的灵活性。如果您无法更改/重构 cmets 中提到的代码,那么您将需要执行针对实际 Smtp 服务的集成测试。
  • 您可以使用Typemock Isolator。但是您要测试什么?

标签: c# unit-testing mocking nunit moq


【解决方案1】:

这就是您应该使用Dependency Injection 的原因之一。

关键是您不应该在SendMail() 中创建SmtpClient 的实例。最好在实现ISmtpClient 接口的SmtpClient 上定义您的包装器,并将该接口传递给BatchProcess 的构造函数,以便您可以在测试中模拟它:

public interface ISmtpClient
{
    int Port { get; set; }

    ICredentialsByHost Credentials { get; set; }

    bool EnableSsl { get; set; }

    void Send(MailMessage mail);
}

public class SmtpClientWrapper : SmtpClient, ISmtpClient
{
}

public class BatchProcess
{
    private readonly ISmtpClient smtpClient;

    BatchProcess(ISmtpClient smtpClient)
    {
        this.smtpClient = smtpClient;
    }

    public void SendMail(List<EmailEntity> emails)
    {
        foreach (EmailEntity email in emails)
        {
            MailMessage mail = new MailMessage();
            mail.From = new MailAddress("your_email_address@gmail.com");
            mail.To.Add(email.ToAddress);
            mail.Subject = email.Subject;
            mail.Body = email.Body;

            //  You could leave this configuration here but it's far better to have it configured in SmtpClientWrapper constructor
            //  or at least outside the loop
            smtpClient.Port = 587;
            smtpClient.Credentials = new System.Net.NetworkCredential("username", "password");
            smtpClient.EnableSsl = true;

            smtpClient.Send(mail);
        }
    }
}

【讨论】:

  • 感谢您的回复。我们不允许更改现有代码。考虑到我如何模拟 SmtpClient
  • 为了编写适当且有价值的单元测试,生产代码应该是“可测试的”。这意味着所有外部资源都应该被抽象出来,这使您可以编写适当隔离的单元测试。在您限制更改生产代码的情况下 - 您可以使用具有模拟所有功能的模拟框架(请注意这些框架不是免费的)。
猜你喜欢
  • 2015-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-04
  • 2017-03-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多