Question

I'm trying to develop an app that sends email, and our internal network is locked down pretty tight, so I can't relay using our internal mail servers.

Has anyone ever used something like no-ip.com? Are there any other alternatives?

Was it helpful?

Solution

If you just need to check that the e-mails are being sent to the correct addresses and with the correct contents, the easiest way is to use a drop folder by editing the app or web.config file:

  <system.net>
    <mailSettings>
      <smtp deliveryMethod="SpecifiedPickupDirectory" from="me@myorg.com">
        <specifiedPickupDirectory pickupDirectoryLocation="C:\TestMailDrop"/>
      </smtp>
    </mailSettings>
  </system.net>

This will result in the e-mails being created as files in the specified directory. You can even then load the files and verify them as part of a unit test.

(As codekaizen points out, this can also be done in code if you don't mind modifying the code/hardcoding the drop folder and having the behavior differing in debug/release mode.)

OTHER TIPS

You can save the email to disk:

#if DEBUG
smtpClient.PickupDirectoryLocation = "\\Path\\to\\save\\folder";
smtpClient.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
smtpClient.Send(msg);
#endif

Riffing from @codekaizen, using AutoFixture.xUnit [which is available as an XUnit package by that name]:-

    [Theory, AutoData]
    public static void ShouldSendWithCorrectValues( string anonymousFrom, string anonymousRecipients, string anonymousSubject, string anonymousBody )
    {
        anonymousFrom += "@b.com";
        anonymousRecipients += "@c.com";

        using ( var tempDir = new TemporaryDirectoryFixture() )
        {
            var capturingSmtpClient = new CapturingSmtpClientFixture( tempDir.DirectoryPath );
            var sut = new EmailSender( capturingSmtpClient.SmtpClient );

            sut.Send( anonymousFrom, anonymousRecipients, anonymousSubject, anonymousBody );
            string expectedSingleFilename = capturingSmtpClient.EnumeratePickedUpFiles().Single();
            var result = File.ReadAllText( expectedSingleFilename );

            Assert.Contains( "From: " + anonymousFrom, result );
            Assert.Contains( "To: " + anonymousRecipients, result );
            Assert.Contains( "Subject: " + anonymousSubject, result );
            Assert.Contains( anonymousBody, result );
        }
    }

CapturingSmtpClientFixture is only used in a test context-

    class CapturingSmtpClientFixture
    {
        readonly string _path;
        readonly SmtpClient _smtpClient;

        public CapturingSmtpClientFixture( string path )
        {
            _path = path;
            _smtpClient = new SmtpClient { DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory, PickupDirectoryLocation = _path };
        }

        public SmtpClient SmtpClient
        {
            get { return _smtpClient; }
        }

        public IEnumerable<string> EnumeratePickedUpFiles()
        {
            return Directory.EnumerateFiles( _path );
        }
    }

All you need to do then is make sure your actual code supplies an SmtpClient that has been wired up with parameters appropriate to the live SMTP server.

(For completeness, here is TemporaryDirectoryFixture):-

public class TemporaryDirectoryFixture : IDisposable
{
    readonly string _directoryPath;

    public TemporaryDirectoryFixture()
    {
        string randomDirectoryName = Path.GetFileNameWithoutExtension( Path.GetRandomFileName() );

        _directoryPath = Path.Combine( Path.GetTempPath(), randomDirectoryName );

        Directory.CreateDirectory( DirectoryPath );
    }

    public string DirectoryPath
    {
        get { return _directoryPath; }
    }

    public void Dispose()
    {
        try
        {
            if ( Directory.Exists( _directoryPath ) )
                Directory.Delete( _directoryPath, true );
        }
        catch ( IOException )
        {
            // Give other process a chance to release their handles
            // see http://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true/1703799#1703799
            Thread.Sleep( 0 );
            try
            {
                Directory.Delete( _directoryPath, true );
            }
            catch
            {
                var longDelayS = 2;
                try
                {
                    // This time we'll have to be _really_ patient
                    Thread.Sleep( TimeSpan.FromSeconds( longDelayS ) );
                    Directory.Delete( _directoryPath, true );
                }
                catch ( Exception ex )
                {
                    throw new Exception( @"Could not delete " + GetType() + @" directory: """ + _directoryPath + @""" due to locking, even after " + longDelayS + " seconds", ex );
                }
            }
        }
    }
}

and a skeleton EmailSender:

public class EmailSender
{
    readonly SmtpClient _smtpClient;

    public EmailSender( SmtpClient smtpClient )
    {
        if ( smtpClient == null )
            throw new ArgumentNullException( "smtpClient" );

        _smtpClient = smtpClient;
    }

    public void Send( string from, string recipients, string subject, string body )
    {
        _smtpClient.Send( from, recipients, subject, body );
    }
}

The usual answer is to run SMTP locally under IIS, but you need to be careful about who you're sending to. It might actually be better to send to your usual SMTP server and target only accounts within your domain.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top