如何编写C#单元测试确保ErrorCodeException错误码唯一
可以通过单元测试实现错误码的唯一性校验
当然可以编写单元测试来完成这个需求,而且推荐用Roslyn静态代码分析的方式来做——这种方式不需要运行业务代码,直接扫描项目里的所有C#源码,精准定位所有抛出ErrorCodeException的位置,同时校验错误码的硬编码属性和唯一性。
具体实现步骤
- 先在测试项目中安装
Microsoft.CodeAnalysis.CSharpNuGet包,用于解析C#语法树。 - 编写测试代码,核心逻辑:
- 遍历目标项目的所有
.cs文件 - 找到所有创建
ErrorCodeException实例的语法节点(即throw new ErrorCodeException(xxx, "xxx")这类代码) - 提取第一个参数(errorCode),检查它是否是常量/字面量(确保是硬编码,而非运行时生成的值)
- 收集所有合法错误码,检查是否存在重复
- 遍历目标项目的所有
示例代码
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Xunit; using System.IO; using System.Collections.Generic; using System.Linq; public class ErrorCodeUniquenessTests { [Fact] public void AllErrorCodeExceptionErrorCodesMustBeUniqueAndHardCoded() { // 替换为你的业务项目根目录路径 var projectRoot = Path.Combine(Directory.GetCurrentDirectory(), "../../../YourBusinessProject"); var csFiles = Directory.EnumerateFiles(projectRoot, "*.cs", SearchOption.AllDirectories); var errorCodes = new Dictionary<int, string>(); foreach (var file in csFiles) { var code = File.ReadAllText(file); var syntaxTree = CSharpSyntaxTree.ParseText(code); var root = syntaxTree.GetRoot(); // 定位所有创建ErrorCodeException的对象初始化节点 var exceptionCreations = root.DescendantNodes() .OfType<ObjectCreationExpressionSyntax>() .Where(oc => oc.Type.ToString() == "ErrorCodeException"); foreach (var creation in exceptionCreations) { // 获取第一个参数(errorCode) var errorCodeArg = creation.ArgumentList.Arguments.FirstOrDefault(); if (errorCodeArg == null) { Assert.Fail($"文件{file}中发现格式错误的ErrorCodeException抛出:缺少errorCode参数"); } // 校验参数是否为常量/字面量 if (!IsConstantExpression(errorCodeArg.Expression)) { Assert.Fail($"文件{file}中发现非硬编码的errorCode:{errorCodeArg.Expression}"); } // 解析错误码数值 if (!int.TryParse(errorCodeArg.Expression.ToString(), out int errorCode)) { Assert.Fail($"文件{file}中无法解析errorCode:{errorCodeArg.Expression}"); } // 检查错误码是否重复 if (errorCodes.ContainsKey(errorCode)) { Assert.Fail($"错误码{errorCode}重复出现:\n首次出现位置:{errorCodes[errorCode]}\n二次出现位置:{file}"); } else { errorCodes.Add(errorCode, file); } } } } // 判断表达式是否为常量/字面量 private bool IsConstantExpression(ExpressionSyntax expr) { // 直接字面量 if (expr is LiteralExpressionSyntax) return true; // 常量字段引用(比如Constants.ErrorCode1) if (expr is MemberAccessExpressionSyntax) { // 如需更精准校验,可以结合语义分析(SemanticModel)判断成员是否为const return true; } // 排除变量、方法调用、计算表达式等运行时生成的值 return false; } }
额外优化建议
如果想在开发阶段就实时提示错误(不用等到跑单元测试),可以基于Roslyn写一个自定义代码分析器,在开发者写代码时就检查错误码的唯一性和硬编码要求,直接在VS中给出警告/错误提示,效率更高。
内容的提问来源于stack exchange,提问作者Tyler
相关产品推荐
相关产品推荐

