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

FluentAssertions自定义EquivalencyStep如何正确打印属性上下文

自定义FluentAssertions EquivalencyStep中属性名称错误显示的解决方法

问题背景

自定义PaginatedList<T>继承自List<T>并实现IPaginatedList接口扩展分页属性,使用FluentAssertions的BeEquivalentTo()时,默认只对比集合内容忽略分页属性。因此编写了PaginatedListEquivalencyStep手动验证这些属性,但验证失败时错误消息中的属性名称显示混乱,仅在添加.WithTrace()后才正常显示正确路径。

原因分析

这不是FluentAssertions的Bug,而是自定义EquivalencyStep的用法存在疏漏:

  • 手动拼接AssertionScope的描述文本时,没有利用FluentAssertions内置的路径管理机制,导致手动拼接的文本与上下文自动追踪的路径发生叠加,生成了混乱的错误消息。
  • .WithTrace()启用了详细路径追踪,临时覆盖了手动拼接的错误,但这并非正确的解决方式。

解决方案

正确的做法是利用验证上下文的CreateChildNode方法生成子属性的路径节点,让FluentAssertions自动维护属性路径,避免手动拼接带来的混乱。

修改后的PaginatedListEquivalencyStep实现

public sealed class PaginatedListEquivalencyStep : EquivalencyStep<IPaginatedList>
{
    protected override EquivalencyResult OnHandle(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator nestedValidator)
    {
        if (comparands.Subject is not IPaginatedList subject)
            throw new NotSupportedException();

        if (comparands.Expectation is not IPaginatedList expectation)
            throw new NotSupportedException();

        // 为每个分页属性创建子节点,让FluentAssertions自动管理路径
        ValidatePaginatedProperty(subject.ItemsCount, expectation.ItemsCount, nameof(IPaginatedList.ItemsCount), context, nestedValidator);
        ValidatePaginatedProperty(subject.CurrentPage, expectation.CurrentPage, nameof(IPaginatedList.CurrentPage), context, nestedValidator);
        ValidatePaginatedProperty(subject.PageSize, expectation.PageSize, nameof(IPaginatedList.PageSize), context, nestedValidator);

        // 验证集合内容
        new EnumerableEquivalencyStep().Handle(comparands, context, nestedValidator);

        return EquivalencyResult.AssertionCompleted;
    }

    private static void ValidatePaginatedProperty(int subjectValue, int expectationValue, string propertyName, IEquivalencyValidationContext context, IEquivalencyValidator nestedValidator)
    {
        var propertyComparands = new Comparands(subjectValue, expectationValue, context.Options);
        var childNode = context.CreateChildNode(propertyName);
        nestedValidator.AssertEquality(propertyComparands, childNode);
    }
}

或者,若仍想使用AssertionScope,需通过CreateChildNode获取正确的属性路径:

protected override EquivalencyResult OnHandle(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator nestedValidator)
{
    if (comparands.Subject is not IPaginatedList subject)
        throw new NotSupportedException();

    if (comparands.Expectation is not IPaginatedList expectation)
        throw new NotSupportedException();

    var itemsCountNode = context.CreateChildNode(nameof(IPaginatedList.ItemsCount));
    using (new AssertionScope(itemsCountNode.Description))
    {
        subject.ItemsCount.Should().Be(expectation.ItemsCount);
    }

    var currentPageNode = context.CreateChildNode(nameof(IPaginatedList.CurrentPage));
    using (new AssertionScope(currentPageNode.Description))
    {
        subject.CurrentPage.Should().Be(expectation.CurrentPage);
    }

    var pageSizeNode = context.CreateChildNode(nameof(IPaginatedList.PageSize));
    using (new AssertionScope(pageSizeNode.Description))
    {
        subject.PageSize.Should().Be(expectation.PageSize);
    }

    new EnumerableEquivalencyStep().Handle(comparands, context, nestedValidator);

    return EquivalencyResult.AssertionCompleted;
}

验证效果

修改后运行测试代码,错误消息将正常显示:

Expected property result.ValueOrDefault.ItemsCount to be 10, but found 3 (difference of -7)

内容的提问来源于stack exchange,提问作者user26719255

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 01:21:02