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

如何用FluentAssertions实现C#嵌套集合的深度相等比较?

问题:嵌套集合的单元测试相等性判定

编写单元测试时遇到以下问题:如何让两个结构完全一致的嵌套集合对象被判定为相等?同时要确保任意深度的嵌套集合按List的索引位置、Dictionary的匹配键进行对等比较。使用FluentAssertions库编写测试代码后出现错误。

代码示例

List<Dictionary<List<string>, string>> list1 = new(){
  new (){{new (){"a"}, "aaa"}, {new (){"z"}, "zzz"}},
  new (){{new (){"b"}, "bbb"}},
  new (){{new (){"c"}, "ccc"}}
};
List<Dictionary<List<string>, string>> list2 = new(){
  new (){{new (){"a"}, "aaa"}, {new (){"z"}, "zzz"}},
  new (){{new (){"b"}, "bbb"}},
  new (){{new (){"c"}, "ccc"}}
};

list1.Should().BeEquivalentTo(list2);

错误信息

未处理的异常:FluentAssertions.Execution.AssertionFailedException:预期list1[0]是包含2个项的字典,但它缺少键{{"a"}, {"z"}}且存在额外键{{"b"}}
预期list1[1]包含键{"b"}。
预期list1[2]包含键{"c"}。

配置信息:
- 使用声明的类型和成员
- 按值比较枚举
- 按属性比较元组
- 按属性比较匿名类型
- 按成员比较记录
- 包含不可浏览成员
- 包含所有非私有属性
- 包含所有非私有字段
- 按名称匹配成员(不匹配则抛出)
- 严格要求字节数组中项的顺序
- 不自动转换。
解决方案

问题根源在于Dictionary的键是List引用类型,默认FluentAssertions会按引用比较键的相等性,而非集合内容。即使两个List内容完全一致,它们是不同的对象实例,因此被判定为不同键,导致字典匹配失败。

要解决这个问题,需配置FluentAssertions对作为字典键的集合类型按内容比较,同时保持List的顺序匹配、字典的键值对对应:

  • 自定义比较规则,指定List<string>类型使用结构相等性比较
  • 确保List严格按索引顺序比较,字典按键匹配对应的值

方案1:直接配置FluentAssertions规则

修改后的测试代码:

list1.Should().BeEquivalentTo(list2, options => options
    .Using<List<string>>(ctx => ctx.Subject.Should().BeEquivalentTo(ctx.Expectation))
    .WhenTypeIs<List<string>>()
    .WithStrictOrdering()); // 强制List按索引顺序比较

方案2:通用嵌套集合配置(适用于任意深度)

list1.Should().BeEquivalentTo(list2, options => options
    .ForAllCollections(c => c.WithStrictOrdering()) // 所有集合类型严格按顺序
    .Using<ICollection>(ctx => ctx.Subject.Should().BeEquivalentTo(ctx.Expectation))
    .WhenTypeIs<ICollection>());

方案3:自定义相等比较器

先实现针对List<string>的相等比较器:

public class ListStringEqualityComparer : IEqualityComparer<List<string>>
{
    public bool Equals(List<string>? x, List<string>? y)
    {
        if (x == null && y == null) return true;
        if (x == null || y == null) return false;
        return x.SequenceEqual(y);
    }

    public int GetHashCode(List<string> obj)
    {
        int hash = 17;
        foreach (var item in obj)
        {
            hash = hash * 31 + (item?.GetHashCode() ?? 0);
        }
        return hash;
    }
}

然后在测试中使用该比较器:

list1.Should().BeEquivalentTo(list2, options => options
    .WithDictionaryKeyComparer(new ListStringEqualityComparer())
    .WithStrictOrdering());

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 14:18:10