Blog

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.

Dave Baskin

View Comments

  • Rarely ever frown, despite the fact that you're the one upsetting, for those who do not no the person falling deeply in love with your ultimate teeth. casquette bulls http://www.c33.fr/

  • I could not figure out how to shim the SMTP client, but adding those Typenames did the trick. Thanks for organizing this post on the subject.

Recent Posts

How to Navigate Azure Governance

 Cloud management is difficult to do manually, especially if you work with multiple cloud…

6 days ago

Why Azure’s Scalability is Your Key to Business Growth & Efficiency

Azure’s scalable infrastructure is often cited as one of the primary reasons why it's the…

3 weeks ago

Unlocking the Power of AI in your Software Development Life Cycle (SDLC)

https://www.youtube.com/watch?v=wDzCN0d8SeA Watch our "Unlocking the Power of AI in your Software Development Life Cycle (SDLC)"…

1 month ago

The Role of FinOps in Accelerating Business Innovation

FinOps is a strategic approach to managing cloud costs. It combines financial management best practices…

1 month ago

Azure Kubernetes Security Best Practices

Using Kubernetes with Azure combines the power of Kubernetes container orchestration and the cloud capabilities…

2 months ago

Mastering Compliance: The Definitive Guide to Managed Compliance Services

In the intricate landscape of modern business, compliance is both a cornerstone of operational integrity…

2 months ago