19

I want to use it to fake System.Net.Mail.SmtpClient in a MS-Test UnitTest. Therefor I added a Fakes Assembmly of System.dll. Then I create a ShimsContext and a StubSmtpClient.

using (ShimsContext.Create())
{
   StubSmtpClient client = new StubSmtpClient();               
}

But what do I do with it? The ultimate goal would be to write a Test which expects that the send Method is called with a object of MailMessage.

user2900970
  • 751
  • 1
  • 5
  • 24
  • Write new class, which implements some needed smtpclient methods and use everywhere that interface. Your stub will also implement that interface. – VikciaR Nov 14 '13 at 06:51
  • You mean like I would do it with every other mocking framework as well? I thought this isn't needed with MS Fakes. – user2900970 Nov 14 '13 at 07:02
  • http://msdn.microsoft.com/en-us/library/hh549175.aspx. Read about Shims. – VikciaR Nov 14 '13 at 08:00
  • I did, but there is no such thing as a `System.Fakes.ShimSmtpClient`. The only Shims are for `Guid` and `DateTime` – user2900970 Nov 14 '13 at 08:50
  • Stubs in Microsoft Fakes are just standard stub objects like any other mocking framework. The main advantage is that Fakes comes with visual studio, uses public delegates for everything, and includes Shims. Shims are powerful in that they let you override methods in classes where you can't reach the origin to stub them, but code that requires them is therefore not well designed. – Magus Nov 18 '13 at 16:43
  • Additionally, if your test is 'was x called' you should probably rethink something. Your test shouldn't know anything but the input and output of a method. Testing that a method has certain code within it is bad because it will break due to a valid logic change (such as an optimization) that does not change the input and output values. – Magus Nov 18 '13 at 16:52
  • Take a look at this Github repo: https://github.com/dfbaskin/SmtpClientFakes – John Koerner Nov 18 '13 at 23:53
  • Another option is an SMTP server running locally. Check out "MailHog" for this exact purpose... now if someone can tell me how to test an SMTP connection which *sometimes* fails so that error handling can be tested.. – LarryBud Jan 15 '21 at 15:54

4 Answers4

36

You can create an interface which will expose functions that will be used from SmtpClient

public interface ISmtpClient   
    {
        void Send(MailMessage mailMessage);
    }

Then create your Concrete class which will do real job.

 public class SmtpClientWrapper : ISmtpClient
    {
        public SmtpClient SmtpClient { get; set; }
        public SmtpClientWrapper(string host, int port)
        {
            SmtpClient = new SmtpClient(host, port);
        }
        public void Send(MailMessage mailMessage)
        {
            SmtpClient.Send(mailMessage);
        }
    }

In your test method now you can mock it and use like;

[TestMethod()]
        public void SendTest()
        {
            Mock<ISmtpClient> smtpClient = new Mock<ISmtpClient>();
            SmtpProvider smtpProvider = new SmtpProvider(smtpClient.Object);
            string @from = "from@from.com";
            string to = "to@to.com";
            bool send = smtpProvider.Send(@from, to);
            Assert.IsTrue(send);
        }
Teoman shipahi
  • 47,454
  • 15
  • 134
  • 158
10

This isn't really an answer to your question, but an alternative aproach:

In the app.config, set up the smtp settings to deliver the mails as files to a local directory instead. Then you can load the file and check the contents in your assert section.

You'll have to write a bit more code for the assertion (or preferably create some helper functions) but you won't have to do anything that affects the production code.

Anders Abel
  • 67,989
  • 17
  • 150
  • 217
6

Another alternative is to use nDumbster:

Install-Package nDumbster

It runs an Smtp server in memory, which you can then verify against. It turns it more into an integration test, but generally those are higher value than unit tests, as you want to test the SmtpClient usage is also correct.

Chris S
  • 64,770
  • 52
  • 221
  • 239
2

A version of this answer by Teoman shipahi that correctly disposes of the SmtpClient.

First make ISmtpClient inherit IDisposable:

public interface ISmtpClient : IDisposable
{
    void Send(MailMessage mailMessage);
}

Then implement IDisposable in the SmtpClientWrapper class:

public class SmtpClientWrapper : ISmtpClient
{
    private bool disposed;
    private readonly SmtpClient smtpClient;

    public SmtpClientWrapper(string host, int port)
    {
        smtpClient = new SmtpClient(host, port);
    }

    ~SmtpClientWrapper()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                smtpClient?.Dispose();
            }
            disposed = true;
        }
    }

    protected void CheckDisposed()
    {
        if (disposed)
        {
            throw new ObjectDisposedException(nameof(SmtpClientWrapper));
        }
    }

    public void Send(MailMessage mailMessage)
    {
        CheckDisposed();
        smtpClient.Send(mailMessage);
    }
}

For this version of SmtpClientWrapper, I have removed the SmtpClient property to avoid the problem of the SmtpClient objects not being disposed of when replaced in setter of the SmtpClient property.

Ɖiamond ǤeezeƦ
  • 3,223
  • 3
  • 28
  • 40