how to mock the SmtpClient object which is used inside a function for Unit Testing

I want to write the Nunit or unit test for the SendMail method which is present in the BatchProcess without sending mails.

How to can I mock the SmtpClient which is present inside another method. Please help.

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("[email protected]");
                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);
            }
        }
    }
}

Upvotes: 2

Views: 4186

Answers (1)

CodeFuller
CodeFuller

Reputation: 31282

That's one of the reason why you should use Dependency Injection.

The point is that you shouldn't create an instance of SmtpClient in SendMail(). It's better to define your wrapper over SmtpClient that implements ISmtpClient interface and pass that interface to constructor of BatchProcess so that you could mock it in the test:

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("[email protected]");
            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);
        }
    }
}

Upvotes: 6

Related Questions