如何为含#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
相关产品推荐
相关产品推荐

