Testing SmtpClient using Microsoft Fakes

A new feature of Visual Studio 2012 is the Microsoft Fakes functionality (well, not a new feature, per se, just a repackaging of the Moles Isolation Framework published by Microsoft Research). It provides a means to isolate functionality in .NET application for testing, including non-virtual and static methods in sealed types. The last part is the key factor.

Previously, to isolate a particular component for testing, you would usually have to factor it in such a way to allow replacing the external dependencies, through interfaces or virtual methods, with mock implementations. With Microsoft Fakes, you can now isolate your code from these external dependencies, including the static and non-virtual methods of these dependencies. (Just a note, by the way, Microsoft Fakes was not the first framework to do this. Typemock has long provided this functionality.)

Let’s look at the SmtpClient class. If I’ve written a component that uses this class, it was hard to test the component since the functionality of StmpClient, on which my class depends, is not easily replaceable or mockable.

Consider a class that provides a simple wrapper around the SmtpClient class:

    public class SendEmail
    {
        public SendEmailStatus Send(EmailInformation emailInfo)
        {
            var status = new SendEmailStatus();
            try
            {
                using (var smtpClient = new SmtpClient())
                {
                    using (var mailMsg = new MailMessage())
                    {
                        mailMsg.From = new MailAddress(emailInfo.FromAddress, emailInfo.FromName);
                        mailMsg.To.Add(new MailAddress(emailInfo.ToAddress, emailInfo.ToName));
                        mailMsg.Subject = emailInfo.Subject;
                        mailMsg.Body = emailInfo.MessageText;
                        mailMsg.IsBodyHtml = emailInfo.IsHtmlMessage;

                        smtpClient.Send(mailMsg);

                        status.WasSent = true;
                    }
                }
            }
            catch (Exception ex)
            {
                status.ErrorMessage = ex.Message;
            }
            return status;
        }
    }

Using Microsoft Fakes, I can create a “shim” for the SmtpClient object which allows me to create a fake instance of the object and specify how the object should perform.

The SmtpClient class lives in the System assembly. To create a shim for this class, I would go to my test project, right-click on the System assembly in the references for the project, and choose “Add Fakes Assembly”. This creates a System.fakes file within the project. I can then edit the file and provide a list of classes that a shim class should be created for:

<Fakes >
  <Assembly Name="System" Version="4.0.0.0"/>
  <ShimGeneration>
    <Add TypeName="SmtpClient"/>
    <Add TypeName="MailMessage"/>
    <Add TypeName="MailAddress"/>
  </ShimGeneration>
</Fakes>

This also adds a new assembly to the project, in this case, System.4.0.0.0.Fakes, that contains the shims (and stubs) for classes in the System assembly. You can in fact browse this assembly like other assemblies within the Object Browser.

So, now I want to test three scenarios for my class:

  • Does my class properly transpose the provided email information into what SmtpClient can use?
  • Does my class return a success status when the email is successfully sent?
  • Does my class return an error message when the email can’t be sent for some reason?

For these tests, I’ll be using shims of the classes I specified above. However, these shims won’t work except in a special ShimsContext:

            using (ShimsContext.Create())
            {
                // ...
            }

This allows the framework to intercept the creation of real objects and replace them with fake objects. You provide the code to configure how the fake objects should perform. For example, this code configures the constructor for the shim SmtpClient class and tells it to do nothing when the SendMailMessage method is called:

                ShimSmtpClient.Constructor =
                    @this =>
                    {
                        var shim = new ShimSmtpClient(@this);
                        shim.SendMailMessage = e => { };
                    };

Once the fake objects are configured, I can then call the class that I want to test, knowing that the external dependencies of my class will use fake objects rather than the concrete classes:

                var emailInfo =
                    new EmailInformation
                    {
                        FromAddress = "from@mail.com",
                        FromName = "From Name",
                        ToAddress = "to@mail.com",
                        ToName = "To Name",
                        Subject = "Email Subject",
                        MessageText = "Email Body",
                        IsHtmlMessage = false,
                    };

                // ...

                var sendEmail = new SendEmail();
                sendEmail.Send(emailInfo);

To test that my class behaves correctly when an attempt is made to send the email, I can specify exactly how the ShimSmtpClient behaves, both for a successful email:

                int emailSendCalled = 0;
                ShimSmtpClient.Constructor =
                    @this =>
                    {
                        var shim = new ShimSmtpClient(@this);
                        shim.SendMailMessage =
                            e =>
                            {
                                ++emailSendCalled;
                            };
                    };

                // ...

                Assert.AreEqual(1, emailSendCalled);

                Assert.IsNotNull(status);
                Assert.AreEqual(true, status.WasSent);
                Assert.IsNull(status.ErrorMessage);

or when an error occurs:

                int emailSendCalled = 0;
                ShimSmtpClient.Constructor =
                    @this =>
                    {
                        var shim = new ShimSmtpClient(@this);
                        shim.SendMailMessage =
                            e =>
                            {
                                ++emailSendCalled;
                                throw new SmtpException("Error sending email.");
                            };
                    };


                // ...

                Assert.AreEqual(1, emailSendCalled);

                Assert.IsNotNull(status);
                Assert.AreEqual(false, status.WasSent);
                Assert.AreEqual("Error sending email.", status.ErrorMessage);

The complete example project can be found at https://github.com/dfbaskin/SmtpClientFakes.

Stay Informed

Sign up for the latest blogs, events, and insights.

We deliver solutions that accelerate the value of Azure.
Ready to experience the full power of Microsoft Azure?

Atmosera is thrilled to announce that we have been named GitHub AI Partner of the Year.

X