使用 xUnit 和 Moq 在 .NET 5 中对 FluentEmail 进行单元测试
Unit test FluentEmail in .NET 5 using xUnit and Moq
我正在尝试进行单元测试 FluentEmail,但我一直在发送响应中收到 null
。
这是ConfigureServices
方法中的设置方式:
// 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() { });
现在电子邮件服务如下所示:
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 } };
}
}
}
我的测试方法是这样的:
[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.");
}
测试总是失败,因为我得到 null
作为 sendResponse
。
有人可以告诉我我这样做是否正确吗?
可能是因为最后一个函数调用 SendAsync
没有被模拟,因此 return 默认为 null。
由于是异步调用,所以使用ReturnsAsync
而不是Returns
是有意义的。
ReturnsAsync
也应该 return 一个实际的 SendResponse
:
//...
SendResponse expectedSendResponse = new SendResponse();
mockFluentEmail
.Setup(m => m.SendAsync(null))
.ReturnsAsync(expectedSendResponse);
//...
我正在尝试进行单元测试 FluentEmail,但我一直在发送响应中收到 null
。
这是ConfigureServices
方法中的设置方式:
// 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() { });
现在电子邮件服务如下所示:
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 } };
}
}
}
我的测试方法是这样的:
[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.");
}
测试总是失败,因为我得到 null
作为 sendResponse
。
有人可以告诉我我这样做是否正确吗?
可能是因为最后一个函数调用 SendAsync
没有被模拟,因此 return 默认为 null。
由于是异步调用,所以使用ReturnsAsync
而不是Returns
是有意义的。
ReturnsAsync
也应该 return 一个实际的 SendResponse
:
//...
SendResponse expectedSendResponse = new SendResponse();
mockFluentEmail
.Setup(m => m.SendAsync(null))
.ReturnsAsync(expectedSendResponse);
//...