Sponsors

News

Jobping Laurent Kempé MVP JetBrains Academy Member Certified ScrumMaster

Contact

My status

View Laurent Kempé's profile on LinkedIn
XING
twitter
facebook


Xbox 360



Map

Locations of visitors to this page

.NET Dudes

Family

French .NET Dudes

Friends

Jobping

Links

Tech Head Brothers

Mocking .NET framework SmtpClient class

This Saturday like the last two I planned to work on my wooden terrace, but with the weather we have for this year's spring, it was almost impossible. So I replaced that with some development.

I am using Rhino.Mocks as mock object framework and went to the following solution to mock SmtpClient.

Capabilities of Rhino.Mock are to mock interfaces, delegates and virtual methods of classes!

My goal was to test my MailService class which use SmtpClient and in particular the method SendAsync, which is not a virtual method. SmtpClient inherit from System.Object so no way to use an interface for the unit test.

Next step was then to make an interface, ISmtpClient, out of the SmtpClient of the .NET Framework using Reflector for .Net. Then I modified the dependency of my MailService class from SmtpClient of the .NET framework to my interface ISmtpClient.

using System.Net;

using System.Net.Mail;

using System.Security.Cryptography.X509Certificates;

using System.Security.Permissions;

 

namespace TechHeadBrothers.Portal.Services.Mail

{

    public interface ISmtpClient

    {

        // Events

        event SendCompletedEventHandler SendCompleted;

 

        // Properties

        X509CertificateCollection ClientCertificates { get; }

        ICredentialsByHost Credentials { get; set; }

        SmtpDeliveryMethod DeliveryMethod { get; set; }

        bool EnableSsl { get; set; }

        string Host { get; set; }

        string PickupDirectoryLocation { get; set; }

        int Port { get; set; }

        ServicePoint ServicePoint { get; }

        int Timeout { get; set; }

        bool UseDefaultCredentials { get; set; }

 

        // Methods

        void Send(MailMessage message);

        void Send(string from, string recipients, string subject, string body);

        [HostProtection(SecurityAction.LinkDemand, ExternalThreading = true)]

        void SendAsync(MailMessage message, object userToken);

        [HostProtection(SecurityAction.LinkDemand, ExternalThreading = true)]

        void SendAsync(string from, string recipients, string subject, string body, object userToken);

        void SendAsyncCancel();

    }

}

Then I wrote the class SmtpClientProxy. It is based on the design pattern Proxy. So it basically maintains a reference, and controls access, to the real SmtpClient so it can be used in place of the real SmtpClient.

namespace TechHeadBrothers.Portal.Services.Mail

{

    public class SmtpClientProxy : ISmtpClient

    {

        private readonly SmtpClient smtpClient;

 

        public SmtpClientProxy()

        {

            smtpClient = new SmtpClient();

            smtpClient.SendCompleted += smtpClient_SendCompleted;

        }

 

        #region ISmtpClient Members

 

        public event SendCompletedEventHandler SendCompleted;

 

        public X509CertificateCollection ClientCertificates

        {

            get { return smtpClient.ClientCertificates; }

        }

 

        public ICredentialsByHost Credentials

        {

            get { return smtpClient.Credentials; }

            set { smtpClient.Credentials = value; }

        }

Finally my MailService class with the dependency injection of ISmtpClient interface and a default constructor using my SmtpClientProxy :

namespace TechHeadBrothers.Portal.Services.Mail

{

    /// <summary>

    /// Service to deliver Emails

    /// </summary>

    public class MailService : IMailService

    {

        private readonly ISmtpClient smtpClient;

 

        public MailService() : this(new SmtpClientProxy())

        {

        }

 

        public MailService(ISmtpClient smtpClient)

        {

            this.smtpClient = smtpClient;

        }

In my unit test I will use the constructor in which I can specify the mock of ISmtpClient, otherwise I will use the default constructor.

So that was for the first issue; having the possibility to mock SmtpClient. Now you certainly have realized the second issue that popped up. In my ISmtpClient I have one event:

    public interface ISmtpClient

    {

        // Events

        event SendCompletedEventHandler SendCompleted;

This event is for sure also in my proxy class, SmtpClientProxy as it inherit form ISmtpClient. In the constructor of SmtpClientProxy I add an event handler on the SendCompleted event of the SmtpClient. This event handler just fires the event exposed by the proxy class, so that I can have an event handler in MailService class to handle the SendComplete event.

        private void smtpClient_SendCompleted(object sender, AsyncCompletedEventArgs e)

        {

            if (SendCompleted != null)

                SendCompleted(sender, e);

        }

Nothing really special. But now the question rise! I need to mock ISmtpClient in my unit test. But my MailService SendMailMessage method call the SmtpClient.SendAsync method and also add an event handler to SmtpClient.SendCompleted event.

        /// <summary>

        /// Sends a MailMessage object using the SMTP settings.

        /// </summary>

        /// <param name="mailMessage">Email message to be sent</param>

        public void SendMailMessage(MailMessage mailMessage)

        {

            try

            {

                mailMessage.IsBodyHtml = true;

                mailMessage.BodyEncoding = Encoding.UTF8;

 

                this.message = mailMessage;

 

                smtpClient.SendCompleted += smtpClient_SendCompleted;

                smtpClient.SendAsync(mailMessage, null);

            }

            catch (SmtpException)

            {

                this.OnEmailFailed(mailMessage);

            }

        }

 

        private void smtpClient_SendCompleted(object sender, AsyncCompletedEventArgs e)

        {

            this.OnEmailSent(message);

        }

In my mock then I need to have the same thing happening, even if I mock the interface ISmtpClient.

Here is the solution I came to:

namespace TechHeadBrothers.Portal.Services.Tests.Mail

{

    [TestFixture]

    public class MailServiceTest

    {

        #region Setup/Teardown

 

        [SetUp]

        public void SetUp()

        {

            mocks = new MockRepository();

        }

 

        #endregion

 

        private MockRepository mocks;

 

        [Test]

        public void SendMailMessageRaiseEmailSentEvent()

        {

            bool emailSentRaised = false;

 

            var message = new MailMessage("lk@test.com", "mk@test.com");

 

            var smtpClient = mocks.Stub<ISmtpClient>();

 

            var mailService = new MailService(smtpClient);

            mailService.EmailSent += ((sender, e) => { emailSentRaised = true; });

 

            using (mocks.Record())

            {

                var raiser = Expect.Call(() => smtpClient.SendCompleted += null)

                                   .IgnoreArguments().GetEventRaiser();

 

                Expect.Call(() => smtpClient.SendAsync(message, null))

                      .Do((Action<MailMessage, object>) ((arg1, arg2) => raiser.Raise(message, null)));

            }

 

            using (mocks.Playback())

            {

                mailService.SendMailMessage(message);

 

                Assert.That(emailSentRaised, Is.EqualTo(true));

            }

        }

    }

}

I create a mock of the interface ISmtpClient. Then I inject this mock into my MailService class. I create a event handler for my MailService.EmailSent event using a lambda. This lambda, if called, will change the boolean value emailSentRaised from false to true. This exactly what I want to test in that unit test; that the EmailSent event is raised.

Then I get raiser object, a Rhino Mock IEventRaiser, from smtpClient.SendCompleted event.

Finally I set an expectation on smtpClient.SendAsync method adding a Do() handler that will raise the SendCompleted event.

Last point is using the mailService object, calling the SendMailMessage method and asserting that my boolean emailSentRaised went from false to true.

And here is the result in ReSharper Unit Test Session window. Green!

 

Update: By the way, I forgot to add a link to a post from Phil Haack "Using Rhino Mocks To Unit Test Events on Interfaces" and another link to a post from Jean-Paul S. Boodhoo "Raising events (from a mock) using Rhino Mocks". To great post to read!

Posted: Jun 01 2008, 12:41 AM by lkempe | with 8 comment(s)
Filed under: ,

Comments

Joe said:

Any particular reason not to simply derive an empty proxy class from SmtpClient (less code):

public class SmtpClientProxy : System.Net.Mail.SmtpClient, ISmtpClient

{

}

Also wouldn't you limit the ISmtpClient interface to the subset of the functionality you're actually going to use in your application.

# June 1, 2008 12:29 PM

lkempe said:

Joe: No particular reason! I guess 1 AM was too late to code ;) You are right for sure! First I can use an empty proxy and for sure I would limit the ISmtpClient interface to the subset of functionality that the application will use.

# June 1, 2008 2:41 PM

labilbe said:

On smtpClient_SendCompleted you should test e.Error and e.Cancelled before to launch the this.OnEmailSent(message);

# November 14, 2008 1:28 PM

Snorre Garmann said:

Hi

I'm still trying to get the grasps on this mocking concept, and what I would like to be able to do is to, in my unit test inspect the content of the message/stream that the SmtpClient would try to send to the server. Any thoughts on how to approach that?

# March 28, 2009 1:27 AM

meno said:

Thanks Laurent. That helped a lot after writing all my unit tests than realizing I was trying to mock a non-virtual method!

# June 26, 2009 2:23 AM

Marco Magdy said:

Great post. Helped me a lot. Thank you.

# July 27, 2009 5:56 PM

Neil R said:

Probably best not to mock it. Even though it's a part of the framework it's almost certainly better to wrap it behind another class which will implement role based interfaces to provide a cleaner facade. You can then mock these interfaces cleanly.

As a rule, and I'm not alone in this, you should only mock types you own.

So technically interesting, but sadly not the right thing to do.

# January 19, 2010 3:19 PM

lkempe said:

Neil R: Good point Neil! I came to your conclusion at later time. And now I use my own interface as a facade, then I mock it.

# January 19, 2010 3:26 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)