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

如何为含#if-#else宏的代码编写单元测试并覆盖全分支

覆盖DEBUG/RELEASE分支的单元测试写法

你的测试代码和被测试代码一样依赖当前项目的编译配置,一次只能覆盖一个分支。要同时覆盖两个分支,有以下几种可行方案:

方案一:分别在DEBUG和RELEASE配置下运行测试

直接保留原Logger的条件编译逻辑,修改测试项目的编译配置,分别生成DEBUG和RELEASE版本并运行测试,就能覆盖两个分支。测试代码可以简化,不用加条件编译:

[Fact]
public void ExceptionTest()
{
    var testException = new DivideByZeroException("测试异常");
    int errorCallCount = 0;
    
    // 临时替换Error方法,统计调用次数
    var originalError = Logger.Error;
    Logger.Error = (tag, msg) => errorCallCount++;

    try
    {
        Logger.Exception("UnitTest", testException);
        
        // 根据当前编译配置断言:DEBUG下调用2次,RELEASE下调用1次
#if DEBUG
        Assert.Equal(2, errorCallCount);
#else
        Assert.Equal(1, errorCallCount);
#endif
    }
    finally
    {
        // 恢复原Error方法
        Logger.Error = originalError;
    }
}

运行测试时,切换测试项目的配置(DEBUG/RELEASE)各跑一次,即可覆盖两个分支。

方案二:解耦日志逻辑,脱离编译配置依赖

修改原Logger类,把条件分支的逻辑改成可配置的开关,这样不用切换编译配置就能测试两个分支:

public static class Logger
{
    // 测试用可配置开关,默认遵循编译符号
    public static bool IncludeStackTrace { get; set; } = #if DEBUG true #else false #endif;

    public static void Exception(string tag, Exception ex)
    {
        Error(tag, $@"Exception: {ex.HResult.ToString("X")} Message: {ex.Message}");
        if (IncludeStackTrace)
        {
            Error(tag, $@"StackTrace: {ex.StackTrace}");
        }
    }

    public static void Error(string tag, string message)
    {
        // 原日志实现
    }
}

然后编写两个独立的测试方法,分别控制开关覆盖两个分支:

public class LoggerTests
{
    [Fact]
    public void Exception_IncludeStackTrace_LogsFullDetails()
    {
        // 准备
        Logger.IncludeStackTrace = true;
        var testEx = new DivideByZeroException("测试异常");
        int errorCallCount = 0;
        var originalError = Logger.Error;
        Logger.Error = (tag, msg) => errorCallCount++;

        try
        {
            // 执行
            Logger.Exception("TestTag", testEx);
            
            // 断言:调用2次Error(基础信息+堆栈)
            Assert.Equal(2, errorCallCount);
        }
        finally
        {
            // 恢复原方法
            Logger.Error = originalError;
        }
    }

    [Fact]
    public void Exception_ExcludeStackTrace_LogsBasicDetails()
    {
        // 准备
        Logger.IncludeStackTrace = false;
        var testEx = new DivideByZeroException("测试异常");
        int errorCallCount = 0;
        var originalError = Logger.Error;
        Logger.Error = (tag, msg) => errorCallCount++;

        try
        {
            // 执行
            Logger.Exception("TestTag", testEx);
            
            // 断言:仅调用1次Error(仅基础信息)
            Assert.Equal(1, errorCallCount);
        }
        finally
        {
            // 恢复原方法
            Logger.Error = originalError;
        }
    }
}

方案三:用编译符号生成针对性测试方法

在测试类中用#if编译符号分别定义DEBUG和RELEASE专属的测试方法,切换配置运行时会自动执行对应测试:

public class LoggerTests
{
#if DEBUG
    [Fact]
    public void Exception_DebugMode_LogsStackTrace()
    {
        var testEx = new Exception("测试异常");
        int callCount = 0;
        var originalError = Logger.Error;
        Logger.Error = (t, m) => callCount++;

        try
        {
            Logger.Exception("DebugTest", testEx);
            Assert.Equal(2, callCount);
        }
        finally
        {
            Logger.Error = originalError;
        }
    }
#endif

#if !DEBUG
    [Fact]
    public void Exception_ReleaseMode_NoStackTrace()
    {
        var testEx = new Exception("测试异常");
        int callCount = 0;
        var originalError = Logger.Error;
        Logger.Error = (t, m) => callCount++;

        try
        {
            Logger.Exception("ReleaseTest", testEx);
            Assert.Equal(1, callCount);
        }
        finally
        {
            Logger.Error = originalError;
        }
    }
#endif
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 12:50:27