NUnit(C#)如何在测试执行前获取测试方法内的所有代码行
实现方案
要在NUnit测试正式执行前提取测试方法内部的代码语句,可以通过NUnit自带的测试拦截能力搭配程序集符号读取实现,不需要修改测试方法本身的逻辑,具体步骤如下:
- 依赖准备
先在测试项目中安装Mono.Cecil、Mono.Cecil.Pdb两个NuGet包,用来读取程序集元数据、调试符号和方法体内容。 - 自定义前置拦截特性
NUnit提供了TestActionAttribute,重写它的BeforeTest方法即可在每个测试用例执行前注入自定义逻辑,在这个方法中可以拿到当前执行测试的方法元数据,再通过Cecil加载程序集和对应的调试符号,就能提取方法内的所有源码语句。
完整实现代码如下:
using NUnit.Framework; using Mono.Cecil; using Mono.Cecil.Cil; using System.Reflection; using System.Linq; using System.Collections.Generic; [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = false)] public class CaptureTestCodeAttribute : TestActionAttribute { public override void BeforeTest(TestDetails testDetails) { MethodInfo currentTestMethod = testDetails.Method; string assemblyPath = currentTestMethod.DeclaringType.Assembly.Location; // 读取程序集并加载调试符号 using var module = ModuleDefinition.ReadModule(assemblyPath, new ReaderParameters { ReadSymbols = true }); // 定位到当前执行的测试方法定义 MethodDefinition targetMethod = module.GetTypes() .First(type => type.FullName == currentTestMethod.DeclaringType.FullName) .Methods .First(method => method.Name == currentTestMethod.Name && method.Parameters.Count == currentTestMethod.GetParameters().Length); List<string> methodStatements = new List<string>(); // 遍历方法体IL指令对应的源码序列点,提取有效代码行 foreach (Instruction instruction in targetMethod.Body.Instructions) { SequencePoint sp = instruction.SequencePoint; if (sp == null) continue; string currentLine; // 优先从文档内存中读行,读不到就从源文件读取 if (sp.Document.Text != null) { currentLine = sp.Document.Text.Split('\n')[sp.StartLine - 1].Trim(); } else { currentLine = File.ReadLines(sp.Document.Url) .Skip(sp.StartLine - 1) .Take(1) .First() .Trim(); } // 过滤空行、方法体大括号 if (string.IsNullOrWhiteSpace(currentLine) || currentLine == "{" || currentLine == "}") continue; // 去重,避免同一行多个指令导致重复记录 if (!methodStatements.Contains(currentLine)) methodStatements.Add(currentLine); } // 到这一步methodStatements里就存了测试方法内的所有有效代码语句,可按需使用 foreach (string statement in methodStatements) { TestContext.WriteLine($"捕获测试代码行:{statement}"); } } public override ActionTargets Targets => ActionTargets.Test; }
- 使用方式
直接把自定义的CaptureTestCodeAttribute标记到对应测试方法、测试类或者整个程序集上即可,对你给出的测试用例,标记后执行前就能捕获全部5条内部语句:
[Test, CaptureTestCode] public void Test01() { abc = 1; pqr(); xyz(); lmn = 9; zxy(); }
注意事项:要正确提取原始C#源码语句,测试项目编译时必须生成PDB调试符号,即项目生成配置中「调试信息」选项不能设为
None,否则只能解析出IL指令,无法获取原始C#代码。如果只需要获取方法的IL执行逻辑,不需要PDB文件即可实现。
内容的提问来源于stack exchange,提问作者Hemant Pawar
相关产品推荐
相关产品推荐

