如何在Fluent Assertions与NUnit单元测试中统计断言次数?
嘿,这个场景我太熟了!之前帮团队处理过类似的自定义断言计数问题,给你几个靠谱的方案,适配不同的团队规模和代码结构:
方案1:直接在自定义断言里加计数(最简方案)
如果你的自定义断言方法数量不多,直接在每个方法里加一行计数代码就行——完全贴合NUnit原生的AssertCount逻辑,不管断言成功还是失败,都算一次调用:
using NUnit.Framework; public static class MyCustomFluentAssertions { public static void ShouldBePositive(this int number) { try { if (number <= 0) throw new AssertionException($"{number} 不是正数!"); } finally { // 不管断言结果如何,都递增计数 TestContext.CurrentContext.AssertCount++; } } // 其他自定义断言方法同理 public static void ShouldBeNotEmpty(this string input) { try { if (string.IsNullOrWhiteSpace(input)) throw new AssertionException("字符串不能为空!"); } finally { TestContext.CurrentContext.AssertCount++; } } }
方案2:用统一包装器减少重复代码(适合大量自定义断言)
如果你们有几十个自定义断言,一个个加太繁琐,写一个通用的断言计数包装器,把所有断言逻辑包进去:
using NUnit.Framework; // 通用计数包装类 internal static class AssertionTracker { public static void Track(Action assertionLogic) { try { assertionLogic.Invoke(); } finally { TestContext.CurrentContext.AssertCount++; } } } // 修改自定义断言,用包装器包裹逻辑 public static class MyCustomFluentAssertions { public static void ShouldBePositive(this int number) { AssertionTracker.Track(() => { if (number <= 0) throw new AssertionException($"{number} 不是正数!"); }); } public static void ShouldBeNotEmpty(this string input) { AssertionTracker.Track(() => { if (string.IsNullOrWhiteSpace(input)) throw new AssertionException("字符串不能为空!"); }); } }
以后新增自定义断言,只要把逻辑塞进AssertionTracker.Track()里就行,不用重复写计数代码。
方案3:基于官方Fluent Assertions扩展(最优雅的长期方案)
如果你们的自定义断言是在官方Fluent Assertions基础上扩展的,直接用官方的NUnit集成就能自动同步计数——根本不用自己写计数逻辑!
首先确保安装了FluentAssertions.NUnit包,然后让你的自定义断言类继承官方的NUnit断言基类:
using FluentAssertions; using FluentAssertions.Numeric; // 自定义断言类,继承官方的NUnit集成基类 public class CustomIntAssertions : NumericAssertions<int> { public CustomIntAssertions(int subject) : base(subject) { } public AndConstraint<CustomIntAssertions> BePositive(string because = "", params object[] becauseArgs) { // 用官方的Execute.Assertion语法,自动被NUnit计数 Execute.Assertion .ForCondition(Subject > 0) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:number} to be positive, but found {0}.", Subject); return new AndConstraint<CustomIntAssertions>(this); } } // 扩展方法,让int类型能调用自定义断言 public static class CustomAssertionExtensions { public static CustomIntAssertions Should(this int number) { return new CustomIntAssertions(number); } }
这种方式下,官方Fluent Assertions会自动和NUnit的TestContext联动,你的自定义断言调用会被自动计入AssertCount,完全符合原生行为。
几个关键注意点:
- 一定要用
finally块或者确保计数代码无论断言成功/失败都会执行——NUnit原生的AssertCount就是这么统计的,哪怕断言抛出异常也算一次调用。 - NUnit的
TestContext.CurrentContext是线程安全的,并行测试场景下也不会计数混乱。 - 如果用方案3,记得所有自定义断言都要基于官方的断言基类,不要自己写独立的异常抛出逻辑,这样才能触发官方的计数机制。
选哪个方案看你们团队的情况:小数量自定义断言用方案1,多数量用方案2,长期维护的话方案3最省心!
内容的提问来源于stack exchange,提问作者Jand
相关产品推荐
相关产品推荐

