如何使用ArchUnitNET与NetArchTest检测.NET解决方案中仅用于单元测试的代码
如何用ArchUnitNET和NetArchTest找出仅被单元测试调用的.NET代码
咱们先理清楚核心需求:你要找出MyTypes命名空间下(排除所有带Test的命名空间),那些仅被测试代码调用、完全没有被业务代码使用的类型或方法。你的现有思路只检查了测试代码是否调用这些方法,或者方法是否被自身命名空间调用,但没抓住“仅被测试调用”的核心——也就是这些代码没有被非测试的业务逻辑引用。
下面分别给你两个工具的正确实现方案:
一、ArchUnitNET 实现方案
ArchUnitNET的核心是定义清晰的类型/方法集合,然后验证业务方法必须被至少一个非测试的业务类型调用——反过来,没被业务类型调用的方法,就是仅被测试调用(或完全未被调用)的目标代码。
// 1. 定义基础类型集合 var businessTypes = Types() .That().ResideInNamespace("^MyTypes.*", true) .And().DoNotResideInNamespace(".*(?i)Test.*", true) .As("非测试业务类型"); var testTypes = Types() .That().ResideInNamespace(".*(?i)Test.*", true) .As("测试类型"); var businessMethods = MethodMembers() .That().AreDeclaredIn(businessTypes) .And().AreNoConstructors() .And().AreNotPrivate() // 只检查公开/内部方法,私有方法通常不会被外部调用 .As("业务类型的公开/内部方法"); // 2. 核心规则:业务方法必须被至少一个业务类型调用(排除测试调用) businessMethods .Should().BeCalledByAny(businessTypes) .Because("业务方法应该被非测试代码使用;如果没有,说明它仅用于测试") .Check(architecture);
说明
- 当检查失败时,ArchUnitNET会直接列出所有没有被业务代码调用的方法,这些就是你要找的“仅用于测试”的代码。
- 如果需要检查整个类型是否仅被测试调用,可以把规则改成验证类型被业务类型引用,而不是方法。
二、NetArchTest 实现方案
NetArchTest需要通过自定义规则来检查方法的调用来源,核心是验证每个业务方法是否存在至少一个来自非Test命名空间的业务调用者。
[Test] public void BusinessMethods_Should_BeUsedByNonTestCode() { var businessTypes = Types.InCurrentDomain() .That().ResideInNamespaceMatching("^MyTypes.*") .And().DoNotResideInNamespaceMatching(".*(?i)Test.*"); var result = businessTypes.Should().MeetCustomRule(new IsUsedByNonTestCode()).GetResult(); Assert.IsTrue(result.IsSuccessful, $"以下类型/方法仅被测试代码调用:\n{string.Join("\n", result.FailingTypeNames)}"); } private class IsUsedByNonTestCode : ICustomRule { private readonly Regex _businessNamespaceRegex = new Regex("^MyTypes.*", RegexOptions.IgnoreCase); private readonly Regex _testNamespaceRegex = new Regex(".*Test.*", RegexOptions.IgnoreCase); public bool MeetsRule(TypeDefinition type) { // 筛选出类型中所有非私有、非构造方法 var targetMethods = type.Methods .Where(m => !m.IsPrivate && !m.IsConstructor); // 如果类型没有符合条件的方法,默认通过(可根据需求调整) if (!targetMethods.Any()) return true; // 验证每个方法是否至少有一个来自业务命名空间的非测试调用者 return targetMethods.All(method => method.Usages.Any(usage => !_testNamespaceRegex.IsMatch(usage.DeclaringType.Namespace) && _businessNamespaceRegex.IsMatch(usage.DeclaringType.Namespace))); } }
说明
- 确保测试运行时加载了所有相关程序集(业务程序集和测试程序集),
InCurrentDomain()才能正确扫描到调用关系。 - 对于标记为
internal的方法,如果测试项目通过InternalsVisibleTo授权访问,NetArchTest也能识别到对应的调用关系。
额外注意点
- 如果要找完全未被任何代码调用的“死代码”,可以在规则中额外排除这种情况(比如检查方法是否有任何调用者,包括测试)。
- 两个工具都需要正确配置程序集扫描范围,ArchUnitNET可以通过
Architecture.LoadAssemblies()指定要扫描的程序集,避免扫描无关代码。
内容的提问来源于stack exchange,提问作者Newnamtab
相关产品推荐
相关产品推荐

