2

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("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);
            }
        }
    }
}
Backs
  • 24,430
  • 5
  • 58
  • 85
  • 1
    You could place it behind an interface ISmtpClient. – lloyd Nov 16 '17 at 03:55
  • Your code is tightly coupled to implementation concerns and make this difficult to test in isolation. There should be a layer of abstraction that would allow more flexibility. If you are unable to change/refactor the code as mentioned in comments then you will need to perform integration tests that hit an actual Smtp service. – Nkosi Nov 16 '17 at 05:32
  • You can use [Typemock Isolator](https://www.typemock.com/docs). But what are you trying to test? – Sam Nov 22 '17 at 12:06

1 Answers1

6

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("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);
        }
    }
}
CodeFuller
  • 30,317
  • 3
  • 63
  • 79
  • Thanks for your response. We are not allowed to change the existing code. Considering that how can I mock the SmtpClient – Surendiran Balasubramanian Nov 16 '17 at 04:44
  • 1
    For writing proper and valuable unit tests, production code should be "testable". That mean all external resources should be abstracted, which give you possibility to write properly isolated unit tests. In your case when you restricted to change production code - you can use mocking frameworks which have feature to mock everything (notice that those frameworks are not free). – Fabio Nov 16 '17 at 07:47