Integration Testing for A SendGrid Wrapper

This post was initially going to be about refactoring a mail wrapper class that I came across, but there was a slight detour so today I will just write about adding a test for this class.

Here is a reproduction of the original code with some hardcoded text changed to protect the innocent:

public static class MailWrapper
{
    public static Task sendMessage(String subj, String content)
    {
        var msg = new SendGridMessage();

        var mailUser = "some@email.com"; // ConfigurationManager.AppSettings["emailFrom"];
        msg.SetFrom(new EmailAddress(mailUser, "site mailer"));

        var recipients = new List
        {
            new EmailAddress("some.other@email.com", "Recipient")
        };
        msg.AddTos(recipients);

        msg.SetSubject(subj);

        msg.AddContent(MimeType.Html, content);

        var apiKey = "someApiKey"; // ConfigurationManager.AppSettings["SENDGRID_APIKEY"];
        var client = new SendGridClient(apiKey);

        return client.SendEmailAsync(msg);
    }
}

This method is then called from an MVC controller:

[HttpPost]
public async Task Index(ContactModel cm)
{
    if (ModelState.IsValid)
    {
        var msg = "Username: " + cm.UserName +
            "<br /> Email: " + cm.Email +
            "<br /> Message: " + cm.Message;
        await MailWrapper.sendMessage("Message from the contact form", msg);
        return View("MailSent");
    }
    return View();
}

There are some issues with this code:

  • Hardcoded (and sensitive) strings
  • The MailWrapper class is in the same project as the MVC code
  • No test code was written
  • The code can be cleaner and more readable
  • The return statement of the sendMessage method appears to return a Response object, but the method signature just returns Task

There is also one good thing which is very important, and that is that the controller is not coupled directly with SendGrid. In this case, if we want to change the third-party library in the future, then there is only one place where this needs to be modified.

So I’ll start first by creating a test. When looking at the code, there doesn’t seem to be enough of an argument at the moment to write a unit test because what the code is mostly doing is calling a third-party library. So in this case, we’ll go with an integration test. This will ensure that when running the tests after a code change or a third-party library update, we will know that sending emails still works.

public class MailWrapperTest
{
    [Fact]
    public async Task Can_Send_A_Message()
    {
        var response = await MailWrapper.sendMessage(
            "This is a test", 
            "Feel free to delete the message as soon as you read it.");

        response.StatusCode.Should().Be(HttpStatusCode.Accepted);
    }
}

I have also decided to change the signature of the sendMessage method so that we can now also check the response:

public static Task sendMessage(String subj, String content)

There is a little side-effect when running the code, as there will be a mail sent to the selected recipient. In a future version, we can also check if this email was successfully received.

0 comments

Add your comments