如何对集合中的属性使用 FluentAssertions 中的“排除”?

我有两门课:

public class ClassA
{
public int? ID {get; set;}
public IEnumerable<ClassB> Children {get; set;}
}


public class ClassB
{
public int? ID {get; set;}
public string Name {get; set;}
}

我想使用流畅的断言来与 ClassA 实例进行比较。但是我想忽略这些 ID (因为这些 ID 将在保存之后分配)。

我知道我能做到:

expectedA.ShouldBeEquivalentTo(actualA, options => options.Excluding(x => x.PropertyPath == "Children[0].ID"));

显然,我可以为集合中的每个 Class B 重复这个步骤。然而,我正在寻找一种方法来排除所有的 ID (而不是对每个元素进行排除)。

但是,如果我删除了[0]索引器,断言就会失败。

这可能吗?

44630 次浏览

简单的方法是直接在集合上设置断言,结合排除 ClassA等价断言:

expectedA.ShouldBeEquivalentTo(expectedB,
o => o.Excluding(s => s.PropertyInfo.Name == "Children"));
expectedA.Children.ShouldBeEquivalentTo(expectedB.Children,
o => o.Excluding(s => s.PropertyInfo.Name = "Id"));

谈什么?

expected.ShouldBeEquivalentTo(actualA, options => options.Excluding(su =>
(su.RuntimeType == typeof(ClassB)) && (su.PropertyPath.EndsWith("Id")));`

或者您可以在属性路径上执行正则表达式匹配,如

expected.ShouldBeEquivalentTo(actualA, options => options.Excluding(su => (Regex.IsMatch
("Children\[.+\]\.ID"));

实际上我喜欢最后一个,但是正则表达式的东西使它有点难以阅读。也许我应该使用一个方法来扩展 ISubjectInfo,使路径与通配符模式相匹配,以便您可以这样做:

expected.ShouldBeEquivalentTo(actualA, options => options
.Excluding(su => su.PathMatches("Children[*].ID")));

我刚刚遇到了一个类似的问题,最新版本的 FluentAssertions 改变了一些东西。

我的对象包含其他对象的字典。字典中的对象包含我想排除的其他对象。我的场景是在测试 Json 序列化时忽略某些属性。

这对我有用:

gotA.ShouldBeEquivalentTo(expectedB , config =>
config
.Excluding(ctx => ctx.SelectedMemberInfo.MemberType == typeof(Venue))
.Excluding(ctx => ctx.SelectedMemberInfo.MemberType == typeof(Exhibit))
.Excluding(ctx => ctx.SelectedMemberInfo.MemberType == typeof(Content))
.Excluding(ctx => ctx.SelectedMemberInfo.MemberType == typeof(Survey))
.Excluding(ctx => ctx.SelectedMemberInfo.MemberType == typeof(Media))
);

我花了一些时间才弄明白怎么做到这一点,但它真的很有用!

最简单的方法是:

expected.ShouldBeEquivalentTo(actual, config => config.ExcludingMissingMembers());

基于从 Dennis Doomen 的回答的正则表达式匹配的想法,我能够使它工作

expected.ShouldBeEquivalentTo(actualA, options =>
options.Excluding(su =>
(Regex.IsMatch(su.SelectedMemberPath, "Children\\[.+\\].ID"));

与 Dennis 的答案不同之处在于: 传递 su. SelectedMemberPath,使用双回斜杠转义方括号。

可以在其中传递表达式列表的扩展类

public static class FluentAssertionsExtensions {
public static EquivalencyAssertionOptions<T> ExcludingNextProperties<T>(
this EquivalencyAssertionOptions<T> options,
params Expression<Func<T, object>>[] expressions) {
foreach (var expression in expressions) {
options.Excluding(expression);
}


return options;
}
}

用法

actual.Should().BeEquivalentTo(expected,
config => config.ExcludingNextProperties(
o => o.Id,
o => o.CreateDateUtc))

ShouldBeEquivalentTo方法现在似乎已经过时了,为了得到 接受的答案的路径,你可以使用 Excluding重载和 IMemberInfo.SelectedMemberPath来代替:

expected.Should().BeEquivalentTo(actualA, options =>
options.Excluding((IMemberInfo mi) => mi.SelectedMemberPath.EndsWith("ID")));

我觉得语法就像

       actual.Should().BeEquivalentTo(
expected,
config => config.Excluding(o => o.Id).Excluding(o => o.CreateDateUtc) });

这里有一些有效的答案,但是我要添加另一个不涉及字符串类型表达式的答案。

expectedA.ShouldBeEquivalentTo(expectedB, o => o.Excluding(s => s.Children));
expectedA.Children.ShouldBeEquivalentTo(expectedB.Children, o => o.Excluding(s => s.Id));
actual.Should().BeEquivalentTo(expected,
assertionOptions => assertionOptions
.Excluding(x => x.CreationTimestamp))

但是,如果您使用的是结构和类重写等于,那么您应该更改默认值,以便与 ComparingByMember 进行比较 Https://fluentassertions.com/objectgraphs/#value-types

actual.Should().BeEquivalentTo(expected,
assertionOptions => assertionOptions
.Excluding(x => x.CreationTimestamp)
.ComparingByMembers<T>())

这是由 FluentAssertions 6.7支持的

actualA.Should().BeEquivalentTo(expectedA, options =>
options
.For(a => a.Children)
.Exclude(b => b.ID));