You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写Mock SmtpClient的单元测试及解决认证失败问题

解决CommunicationService单元测试中“Authentication unsuccessful”错误

问题根源

  • 你的CommunicationService的SendMail方法完全没有使用构造函数注入的ISmtpClient实例,而是在方法内部直接创建了真实的SmtpClient并调用其Send方法,导致你Mock的ISmtpClient完全失效。代码实际在尝试连接真实的SMTP服务器(smtp.office365.com),从而触发认证失败错误。
  • 你定义的ISmtpClient和SmtpClientWrapper没有被正确利用,依赖注入的设计完全没发挥作用。

修复步骤

  1. 重构CommunicationService,使用注入的ISmtpClient

    • 删除方法内创建SmtpClient和SmtpClientWrapper的代码,直接使用构造函数注入的_client实例。
    • SMTP配置(Host、Port等)应该通过构造函数注入配置类,而非在业务方法内硬编码。
  2. 调整单元测试,正确Mock ISmtpClient的Send行为

    • 无需手动创建测试用的MailMessage,让Mock匹配任意MailMessage实例,或验证传入的邮件参数符合预期。

修改后的代码示例

修复后的CommunicationService

public class CommunicationService : ICommunicationService
{
    private readonly ILogger _logger;
    private readonly ISmtpClient _client;

    // 可注入SMTP配置类替代硬编码参数
    public CommunicationService(ILogger logger, ISmtpClient client)
    {
        _logger = logger;
        _client = client;
    }

    public bool SendMail(string mailSender, IEnumerable<string> recipients, IEnumerable<string> cc, string subject, string mailContent, string senderPass)
    {
        MailMessage mailMessage = new MailMessage
        {
            Subject = subject,
            Body = mailContent,
            From = new MailAddress(mailSender, "XXXX"),
            IsBodyHtml = true
        };

        var recipientsList = recipients.ToList();
        if (!recipientsList.Any())
        {
            _logger.LogWarning("Mail not sent, no recipients specified");
            return false; // 无收件人直接返回,避免后续无效操作
        }

        foreach (var address in recipientsList)
        {
            mailMessage.To.Add(address);
        }

        if (cc != null)
        {
            foreach (string address in cc)
            {
                mailMessage.CC.Add(address);
            }
        }

        try
        {
            // 使用注入的ISmtpClient,不再创建真实SmtpClient
            _client.Send(mailMessage);
            return true;
        }
        catch (Exception exception)
        {
            _logger.LogError(exception, exception.Message);
            return false;
        }
    }
}

修复后的单元测试

[Test]
public void SendMail_SuccessfullySendsMail_ReturnsTrue()
{
    // Arrange
    var mailSender = "your-email@example.com";
    var recipients = new List<string> { "recipient1@example.com", "recipient2@example.com" };
    var cc = new List<string> { "cc1@example.com", "cc2@example.com" };
    var subject = "Test Subject";
    var mailContent = "Test Body";
    var senderPass = "your-password";

    var loggerMock = new Mock<ILogger<CommunicationService>>();
    var clientMock = new Mock<ISmtpClient>();
    // 匹配任意MailMessage实例,也可添加参数验证逻辑
    clientMock.Setup(x => x.Send(It.IsAny<MailMessage>())).Verifiable();

    var service = new CommunicationService(loggerMock.Object, clientMock.Object);

    // Act
    var result = service.SendMail(mailSender, recipients, cc, subject, mailContent, senderPass);

    // Assert
    Assert.IsTrue(result);
    clientMock.Verify(x => x.Send(It.IsAny<MailMessage>()), Times.Once); // 验证Send方法被调用一次
}

额外优化建议

  • 不要在SendMail方法内直接创建NetworkCredential,可将认证凭证通过配置类注入到ISmtpClient的实现中。
  • 为SmtpClientWrapper添加SMTP配置的构造参数(Host、Port、Credential等),让配置从外部注入,避免硬编码。
  • 单元测试中可添加邮件内容验证,比如检查收件人、主题是否正确:
clientMock.Setup(x => x.Send(It.Is<MailMessage>(m => 
    m.Subject == subject && 
    m.To.Any(a => a.Address == "recipient1@example.com"))));

内容的提问来源于stack exchange,提问作者Bash

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 13:25:38