Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock/fake SmtpClient in a UnitTest?

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.

like image 449
user2900970 Avatar asked Nov 14 '13 06:11

user2900970


4 Answers

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 = "[email protected]";
            string to = "[email protected]";
            bool send = smtpProvider.Send(@from, to);
            Assert.IsTrue(send);
        }
like image 67
Teoman shipahi Avatar answered Nov 07 '22 11:11

Teoman shipahi


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.

like image 39
Anders Abel Avatar answered Nov 07 '22 11:11

Anders Abel


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.

like image 6
Chris S Avatar answered Nov 07 '22 12:11

Chris S


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.

like image 2
Ɖiamond ǤeezeƦ Avatar answered Nov 07 '22 11:11

Ɖiamond ǤeezeƦ