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

如何在测试时规避方法中的Fatal()调用?

优雅解决测试时跳过Log.Fatal()的邮件发送与程序终止问题

最推荐的方案是通过接口抽象+依赖注入解耦日志逻辑,彻底隔离生产环境与测试环境的日志行为,避免冗余的条件判断和命令行参数。

1. 抽象日志接口

先把原有的静态Log功能抽象成接口,将生产环境的真实实现与接口分离:

public interface ILogger
{
    void Fatal(string message);
}

// 生产环境日志实现(复用原有逻辑)
public class ProductionLogger : ILogger
{
    public void Fatal(string message)
    {
        // 调用原有的会发送邮件、终止程序的Log.Fatal
        Log.Fatal(message);
    }
}

2. 重构业务代码,注入日志接口

修改你的SQL执行类,通过构造函数注入ILogger实例,不再直接调用静态Log:

public class SqlQueryExecutor
{
    private readonly ILogger _logger;
    private readonly SqlConnection _conn;

    // 构造函数注入ILogger,解耦日志依赖
    public SqlQueryExecutor(ILogger logger, SqlConnection conn)
    {
        _logger = logger;
        _conn = conn;
    }

    public void ExecuteQuery(Stream stream, int timeoutInSeconds, IEnumerable<string> parameters)
    {
        try
        {
            using (StreamReader reader = new(stream))
            {
                var commandText = reader.ReadToEnd();

                SqlCommand cmd = new(commandText, _conn)
                {
                    CommandType = CommandType.Text,
                    CommandTimeout = timeoutInSeconds
                };

                foreach (string s in parameters)
                {
                    string[] fields = s.Split(':');
                    cmd.Parameters.AddWithValue(fields[0], fields[1]);
                }

                cmd.ExecuteNonQuery();
            }
        }
        catch (Exception ex)
        {
            // 调用注入的日志实例的Fatal方法
            _logger.Fatal("Failed to execute query: " + ex.Message);
        }
    }
}

3. 单元测试时使用Mock日志

用Mock框架(比如Moq)创建ILogger的模拟实例,既可以验证Fatal方法是否被调用,又不会触发真实的邮件发送和程序终止逻辑:

[Test]
public void WhenQueryExecutionFails_FatalMethodIsCalled()
{
    // 准备Mock对象
    var mockLogger = new Mock<ILogger>();
    var mockConn = new Mock<SqlConnection>();
    
    // 模拟SqlCommand执行抛出异常
    var mockCmd = new Mock<SqlCommand>();
    mockCmd.Setup(cmd => cmd.ExecuteNonQuery()).Throws(new SqlException());
    mockConn.Setup(conn => conn.CreateCommand()).Returns(mockCmd.Object);

    // 创建业务类实例,注入Mock日志
    var executor = new SqlQueryExecutor(mockLogger.Object, mockConn.Object);
    var testStream = new MemoryStream(Encoding.UTF8.GetBytes("INVALID SQL"));

    // 执行测试方法
    executor.ExecuteQuery(testStream, 30, new List<string>());

    // 验证Fatal方法是否被调用了一次
    mockLogger.Verify(logger => logger.Fatal(It.IsAny<string>()), Times.Once);
}

备选方案:条件编译(适合小范围改动)

如果不想大规模重构代码,可以通过定义编译符号实现测试环境的逻辑隔离:

  1. 在测试项目的属性中添加编译符号TEST_MODE
  2. 修改业务代码中的异常捕获逻辑:
catch (Exception ex)
{
#if !TEST_MODE
    Log.Fatal("Failed to execute query: " + ex.Message);
#else
    // 测试环境下仅记录验证信息,不触发真实逻辑
    Debug.WriteLine($"[Test] Fatal would be triggered with message: {ex.Message}");
#endif
}

这个方案的缺点是无法直接验证Fatal的调用情况,灵活性不如依赖注入方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 00:22:43