Search code examples
asp.nettestingasp.net-coreintegration-testing

Strange exception testing EmailSender


I have just closely followed all MS instructions on scaffolding the Identity UI in an ASP.NET Core 3.0 MVC app. If I run the project, it opens the default home page fine, and links on that page respond well. Yet a simple integration test fails with an exception.

The single method in the EmailSender looks like this:

public Task SendEmailAsync(string email, string subject, string htmlMessage)
{
    using (var client = new SmtpClient())
    using (var msg = new MailMessage())
    {
        msg.From = new MailAddress("[email protected]");
        msg.To.Add(new MailAddress(email));
        msg.Subject = subject;
        msg.IsBodyHtml = true;

        client.UseDefaultCredentials = false;
        client.Credentials = new NetworkCredential("[email protected]", "something");
        client.Host = "mail.myhost.com";
        client.Port = 25;
        return client.SendMailAsync(msg);
    }
}

and my test looks like this:

[TestMethod]
public async Task Send_Email()
{
    var sender = new EmailSender();

    await sender.SendEmailAsync("[email protected]", "Test Mail", "This is a test");

    Assert.IsTrue(true);
}

When I run the test it fails with the following exception:

Test method TimeKeeper.Tests.Integration.EmailSEnderTests.Send_Email threw exception: 
System.Threading.Tasks.TaskCanceledException: A task was canceled.
    at TimeKeeper.Tests.Integration.EmailSEnderTests.Send_Email() in D:\Dev\RestServices\TimeKeeper.Tests.Integration\EmailSEnderTests.cs:line 15
   at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.ThreadOperations.ExecuteWithAbortSafety(Action action)

Solution

  • await the task instead of returning it.

    public async Task SendEmailAsync(string email, string subject, string htmlMessage) {
        using (var client = new SmtpClient())
        using (var msg = new MailMessage()) {
            msg.From = new MailAddress("[email protected]");
            msg.To.Add(new MailAddress(email));
            msg.Subject = subject;
            msg.IsBodyHtml = true;
    
            client.UseDefaultCredentials = false;
            client.Credentials = new NetworkCredential("[email protected]", "something");
            client.Host = "mail.myhost.com";
            client.Port = 25;
            await client.SendMailAsync(msg); //<-- await completion
        }
    }
    

    Chances are that the client is being disposed before it has a chance to complete sending the email.

    You should also take note of the warnings in documentation about using the now obsolete SmtpClient for new development.

    DE0005: SmtpClient shouldn't be used

    Motivation

    SmtpClient doesn't support many modern protocols. It is compat-only. It's great for one off emails from tools, but doesn't scale to modern requirements of the protocol.

    Recommendation

    Use MailKit or other libraries.