Search code examples
c#asp.netasp.net-corenunitsmtpclient

How to write unit test case and Mock SmtpClient Send mail


I am writing unit test case for the below class Communication service. I have the below interface communicationservice .
I want to write the Nunit or unit test for the SendMail method which is present in the CommunicationService without sending mails. (smtpClient.Send(mailMessage))


```
public interface ISmtpClient
    {
        void Send(MailMessage message);
    }

    public class SmtpClientWrapper : ISmtpClient
    {
        public SmtpClientWrapper(SmtpClient client)
        {
            Client = client;
        }

        public SmtpClient Client { get; }

        public void Send(MailMessage message)
        {
            Client.Send(message);
        }
    }
    public interface ICommunicationService
    {
        bool SendMail(string mailSender, IEnumerable<string> recipients, IEnumerable<string> cc, string subject, string mailContent, string senderPass);
    }

Implementation logic for the communication service parameter here are the details needed for Mail message class. for send mail public class CommunicationService : ICommunicationService { private readonly ILogger _logger; private readonly ISmtpClient _client; public CommunicationService( ILogger logger, ISmtpClient client) { _logger = logger; _client = client; // also read appsettings for the smtp client details } public bool SendMail(string mailSender, IEnumerable recipients, IEnumerable cc, string subject, string mailContent, string senderPass) { MailMessage mailMessage = new MailMessage { Subject = subject, Body = mailContent, From = new MailAddress(mailSender, "XXXX"), IsBodyHtml = true };

            var recipientsList = recipients.ToList();
            if (!recipientsList.Any())
            {
                _logger.LogWarning("Mail not sent, no recipients specified");
            }

            foreach (var address in recipientsList)
            {
                mailMessage.To.Add(address);
            }

            if (cc != null)
            {
                foreach (string address in cc)
                {
                    mailMessage.CC.Add(address);
                }
            }
            NetworkCredential networkCredential = new NetworkCredential(mailSender, senderPass);


            try
            {

                var smtp = new SmtpClient
                {
                    Host = "smtp.office365.com",
                    Port = 587,
                    EnableSsl = true,
                    UseDefaultCredentials = false,
                    Credentials = networkCredential,
                    DeliveryMethod = SmtpDeliveryMethod.Network,
                    Timeout = 20000,

                };
                var wrapper = new SmtpClientWrapper(smtp);

                smtp.Send(mailMessage);

                return true;
            }

            catch (Exception exception)
            {

                _logger.LogError(exception.Message, exception);
                return false;
            }
        }
    }
From the consumer i call the communication service send method with the details needed for Mail message.Passing here the parameters needed for MailMessage class



here is the code i have tried. Here is the unit test case 



    public void SendMail_SuccessfullySendsMail_ReturnsTrue()
        {
           
            // Arrange
           
            var mailSender = "[email protected]";
            var recipients = new List<string> { "[email protected]", "[email protected]" };
            var cc = new List<string> { "[email protected]", "[email protected]" };
            var subject = "Test Subject";
            var mailContent = "Test Body";
            var senderPass = "your-password";
            MailMessage mail = new MailMessage()
            {
                Body = "ABC",
                Subject = subject

            };
            var clientMock = new Mock<ISmtpClient>();
           // clientMock.Setup(x => x.Send(mail));

            // Create an instance of your MailSender class
            var mailSenderInstance = new CommunicationService(logger.Object, clientMock.Object);

            // Act
            var result = mailSenderInstance.SendMail(mailSender, recipients, cc, subject, mailContent, senderPass);



            
          
               Assert.IsTrue(result);


        }



Here is the unit test case, but it not work . Authentication unsuccessful message i am getting from the nunit test case. 

Solution

  • You can rewrite your class like this and use SmtpClientWrapper in your production code:

     public interface ISmtpClient
     {
        void Send(MailMessage message);
     }
    
    public class SmtpClientWrapper : ISmtpClient
    {
        public SmtpClientWrapper(SmtpClient client)
        {
            Client = client;
        }
    
        public SmtpClient Client { get; }
    
        public void Send(MailMessage message)
        {
            Client.Send(message);
        }
    }
    
    
    public class CommunicationService: ICommunicationService 
    { 
        private readonly ILogger _logger; 
        private readonly ISmtpClient _client; 
        public CommunicationService( ILogger logger, ISmtpClient client) 
        { 
            _logger = logger;
            _client = client;
        }
    
         public bool SendMail(string mailSender, IEnumerable recipients, IEnumerable cc, string subject, string mailContent, string senderPass)
         {
              // validation part of the old method here
              
              // use client from constructor
              _client.Send(mailMessage);
         }
        }
    

    Test method:

    public void SendMail_SuccessfullySendsMail_ReturnsTrue()
        {
             // Arrange
                var clientMock = new Mock<ISmtpClient>();
                clientMock.Setup(x => x.Send(new MailMessage()));
    
                var mailSender = "[email protected]";
                var recipients = new List<string> { "[email protected]", "[email protected]" };
                var cc = new List<string> { "[email protected]", "[email protected]" };
                var subject = "Test Subject";
                var mailContent = "Test Body";
                var senderPass = "your-password";
    
                // Create an instance of your MailSender class
                var mailSenderInstance = new CommunicationService(logger.Object, smtpClient.Object);
            
                // Act
                var result = mailSenderInstance.SendMail(mailSender, recipients, cc, subject, mailContent, senderPass);
    
                // Assert
                Assert.IsTrue(result);
    
        }
     
    

    There is also another thread on mocking the SmtpClient (which may provide more elegant solution): How to mock/fake SmtpClient in a UnitTest?