1

I'm trying to unit test FluentEmail, but I keep getting null in the send response.

This is how it's set up in the ConfigureServices method:

// Set email service using FluentEmail
services.AddFluentEmail("sender@test.com")
        // For Fluent Email to find _Layout.cshtml, just mention here where your views are.
        .AddRazorRenderer(@$"{Directory.GetCurrentDirectory()}/Views/")
        .AddSmtpSender("smtp.somecompanyname.com", 25)
        .AddSmtpSender(new System.Net.Mail.SmtpClient() { });

Now the email service looks like this:

public class FluentEmailService : IFluentEmailService
{
    private readonly IFluentEmail _fluentEmail;
    private readonly ILogger<FluentEmailService> _logger;
    public FluentEmailService(ILogger<FluentEmailService> logger, IFluentEmail fluentEmail)
    {
        _logger = logger;
        _fluentEmail = fluentEmail;
    }

    public async Task<SendResponse> SendEmailAsync<TModel>(string subject, string razorTemplatePath, TModel model, string semicolonSeparatedEmailRecipients)
    {
        try
        {
            var sendResponse = await _fluentEmail
                            .To(semicolonSeparatedEmailRecipients)
                            .Subject(subject)
                            .UsingTemplateFromFile(razorTemplatePath, model)
                            .SendAsync();
            return sendResponse;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to send email. Check exception for more information.");
            return new SendResponse() { ErrorMessages = new string[] { ex.Message } };
        }
    }
}

My test method looks like this:

[Fact]
public async Task Can_Verify_FluentEmail_WORKS_Async()
{
    // ARRANGE
    var mockFluentEmail = new Mock<IFluentEmail>();

    mockFluentEmail.Setup(m => m.To(It.IsAny<string>())).Returns(mockFluentEmail.Object);
    mockFluentEmail.Setup(m => m.Subject(It.IsAny<string>())).Returns(mockFluentEmail.Object);
    mockFluentEmail.Setup(m => m.UsingTemplateFromFile(It.IsAny<string>(), It.IsAny<It.IsAnyType>(), It.IsAny<bool>())).Returns(mockFluentEmail.Object);

    //Create FluentEmail service using fake logger and IFluentEmail
    var fakeLogger = Mock.Of<ILogger<FluentEmailService>>();
    var fluentEmailService = new FluentEmailService(fakeLogger, mockFluentEmail.Object);

    // ACT
    var sendResponse = await fluentEmailService.SendEmailAsync("Test Subject", "Some Path", It.IsAny<It.IsAnyType>(), "Some Recipient");
    
    // ASSERT
    Assert.NotNull(sendResponse);
    Assert.True(sendResponse.Successful);
    mockFluentEmail.Verify(f => f.To("Some Recipient"), Times.Once(), "Recipient should be set as: 'Some Recipient'.");
    mockFluentEmail.Verify(f => f.Subject("Test Subject"), Times.Once, "Subject should be set as: 'Test Subject'.");
    mockFluentEmail.Verify(f => f.UsingTemplateFromFile("Some Path", It.IsAny<It.IsAnyType>(), It.IsAny<bool>()), Times.Once, "Path should be set as: 'Some Path'.");
    mockFluentEmail.Verify(f => f.SendAsync(null), Times.Once, "1 email should be sent.");
}

The test always fails because I get null as the sendResponse.

Can someone please tell me if I'm doing this correctly?

Ash K
  • 1,802
  • 17
  • 44

1 Answers1

2

It is probably because of last function call SendAsync is not mocked and therefore returning null by default.

Since it is async call, it makes sense to use ReturnsAsync instead of Returns.

ReturnsAsync should also return an actual SendResponse:

//...

SendResponse expectedSendResponse = new SendResponse();

mockFluentEmail
   .Setup(m => m.SendAsync(null))
   .ReturnsAsync(expectedSendResponse);

//...
Nkosi
  • 235,767
  • 35
  • 427
  • 472
Eugene
  • 1,487
  • 10
  • 23