.NET 5中使用xUnit与Moq单元测试FluentEmail返回null如何解决
问题原因及修复方案
核心问题(直接导致sendResponse为null)
你没有对IFluentEmail的SendAsync方法配置Mock返回值,Moq对于未显式Setup的异步方法,默认返回null,所以你执行await SendAsync()后拿到的结果就是空。
其他存在的问题
- 链式方法
UsingTemplateFromFile的Setup参数不匹配:你业务代码调用的是双参数重载,测试Setup时写的三参数重载(第三个是可选的是否母版页参数),可能导致匹配失败,Mock返回默认值。 - 测试执行阶段错误使用
It.IsAny<It.IsAnyType>()作为入参:It.IsAny系列方法仅可用于Moq的Setup和Verify场景,不能作为实际方法调用的入参使用。 SendAsync验证时参数匹配错误:SendAsync的入参是CancellationToken类型,你写的SendAsync(null)无法匹配默认调用的SendAsync(CancellationToken.None),会导致验证不通过。- 生产配置冗余(不影响单元测试,但会导致线上功能异常):你的
ConfigureServices中重复注册了两次SmtpSender,实际运行时只会生效最后一个空配置的SmtpClient,导致生产环境邮件发送失败。
修正后的测试代码
[Fact] public async Task Can_Verify_FluentEmail_WORKS_Async() { // 准备阶段 var mockFluentEmail = new Mock<IFluentEmail>(); // 构造模拟的成功返回结果 var mockSendResponse = new SendResponse { Successful = true }; mockFluentEmail.Setup(m => m.To(It.IsAny<string>())).Returns(mockFluentEmail.Object); mockFluentEmail.Setup(m => m.Subject(It.IsAny<string>())).Returns(mockFluentEmail.Object); // 匹配双参数重载的UsingTemplateFromFile mockFluentEmail.Setup(m => m.UsingTemplateFromFile(It.IsAny<string>(), It.IsAny<object>())) .Returns(mockFluentEmail.Object); // 新增SendAsync的Setup,返回模拟的成功结果 mockFluentEmail.Setup(m => m.SendAsync(It.IsAny<CancellationToken>())) .ReturnsAsync(mockSendResponse); // 使用伪造的logger和IFluentEmail实例创建FluentEmail服务 var fakeLogger = Mock.Of<ILogger<FluentEmailService>>(); var fluentEmailService = new FluentEmailService(fakeLogger, mockFluentEmail.Object); // 执行阶段:替换It.IsAny为实际的测试model,比如随便一个匿名类 var testModel = new { }; var sendResponse = await fluentEmailService.SendEmailAsync("Test Subject", "Some Path", testModel, "Some Recipient"); // 断言阶段 Assert.NotNull(sendResponse); Assert.True(sendResponse.Successful); mockFluentEmail.Verify(f => f.To("Some Recipient"), Times.Once(), "收件人应为:'Some Recipient'"); mockFluentEmail.Verify(f => f.Subject("Test Subject"), Times.Once, "主题应为:'Test Subject'"); mockFluentEmail.Verify(f => f.UsingTemplateFromFile("Some Path", It.IsAny<object>()), Times.Once, "模板路径应为:'Some Path'"); // 修正SendAsync的参数匹配 mockFluentEmail.Verify(f => f.SendAsync(It.IsAny<CancellationToken>()), Times.Once, "应触发1次邮件发送"); }
生产配置修正建议
删除重复的AddSmtpSender注册,保留一个有效配置即可:
services.AddFluentEmail("sender@test.com") .AddRazorRenderer(@$"{Directory.GetCurrentDirectory()}/Views/") .AddSmtpSender("smtp.somecompanyname.com", 25);
内容的提问来源于stack exchange,提问作者Ash K
相关产品推荐
相关产品推荐

