如何在测试时规避方法中的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); }
备选方案:条件编译(适合小范围改动)
如果不想大规模重构代码,可以通过定义编译符号实现测试环境的逻辑隔离:
- 在测试项目的属性中添加编译符号
TEST_MODE - 修改业务代码中的异常捕获逻辑:
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
相关产品推荐
相关产品推荐

