You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:09:50