FluentAssertions的ExcludingNestedObjects选项行为不符预期的技术咨询
FluentAssertions中ExcludingNestedObjects方法的疑问与解决
问题场景
先看这段C#代码:
class Parent { public int Id { get; set; } public Child Child { get; set; } } class Child { public int Id { get; set; } } var p1 = new Parent { Id = 0, Child = new Child { Id = 1 } }; var p2 = new Parent { Id = 0, Child = new Child { Id = 2 } }; p1.Should().BeEquivalentTo(p2);
如预期,断言失败,提示:
Expected property p1.Child.Id to be 2, but found 1.
把断言改成带ExcludingNestedObjects()的版本:
p1.Should().BeEquivalentTo(p2, o => o.ExcludingNestedObjects());
结果断言还是失败,提示:
Expected property p1.Child to be Child
{
Id = 2
}, but found Child
{
Id = 1
}
按照方法的帮助文本,ExcludingNestedObjects()应该让结构相等检查排除嵌套集合和复杂类型,但Child作为复杂类型还是被检查了,这是为什么?
目前能让断言通过的方式是逐个排除复杂类型属性:
p1.Should().BeEquivalentTo(p2, o => o.Excluding(p => p.Child));
但如果有大量复杂类型属性,逐个排除太麻烦。
原因解析
ExcludingNestedObjects()的实际作用和帮助文本的描述有偏差,它不会完全跳过复杂类型属性的检查,而是改变了检查逻辑:
- 原本会递归检查嵌套对象的所有内部属性
- 启用该方法后,停止递归检查,转而使用对象的默认相等规则来判断这个嵌套对象本身是否相等
对于自定义的Child类,因为没有重写Equals和GetHashCode方法,默认会用引用相等性来比较。p1.Child和p2.Child是两个不同的实例,引用地址不一样,所以断言判定它们不相等,导致失败。
批量排除复杂类型的解决方案
如果要批量排除所有自定义复杂类型的属性,可以通过筛选规则实现,不用逐个指定属性:
p1.Should().BeEquivalentTo(p2, options => options .Excluding(ctx => !ctx.SelectedMemberInfo.PropertyType.IsValueType && !ctx.SelectedMemberInfo.PropertyType.Equals(typeof(string))));
这个规则会排除所有非值类型且非字符串的属性,也就是所有自定义的复杂类对象,一次性解决批量排除的需求。
如果需要更灵活的比较逻辑,还可以自定义IEquivalencyStep来扩展,但上述方式已经能覆盖大部分场景。
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

