Bash
Bash

Reputation: 13

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. 

Upvotes: 1

Views: 1159

Answers (1)

vhr
vhr

Reputation: 1664

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?

Upvotes: 0

Related Questions