如何比较通过 ID 链接的对象图?

Posted

技术标签:

【中文标题】如何比较通过 ID 链接的对象图?【英文标题】:How do I compare object graphs that are linked by means of IDs? 【发布时间】:2020-06-12 16:06:02 【问题描述】:

Fluent Assertions 库通过Should().BeEquivalentTo 方法和相关方法非常强调其comparing object graphs 的功能。正如文档指出的那样:

Should().BeEquivalentTo 是一个非常强大的功能,也是 Fluent Assertions 的独特卖点之一。

通过阅读示例,我得到的印象是对象图必须通过对象引用来连接。

现在,我有很多我想检查其内容的对象图和树,但它们通常是从文件或数据库中加载的。 因此,连接仅通过跨对象匹配的 ID 属性值存在。

例如,想象一下这个简单的结构(这里以 JSON 格式编写)- 示例 A:

[
    "Id": 1,
    "Name": "Foo",
    "NextId": 2
, 
    "Id": 2,
    "Name": "Bar"
]

我想使用 Fluent Assertions 来检查此类对象图的结构。 也就是说,无论IdNextId 字段实际具有什么值,第一个对象中的NextId 必须与第二个对象中的Id 具有相同的值。 因此,任何具有两个不同 Id 值的两个对象的集合,其中一个对象的 NextId 值等于另一个对象的 Id 值,都可以。

示例 B:

[
    "Key": 5,
    "LinkedTo": [3]
, 
    "Key": 10,
    "LinkedTo": []
, 
    "Key": 3,
    "LinkedTo": [5]
]

在这里,具有三个不同Key 值的三个对象的任何集合,其中两个对象在其LinkedTo 属性中引用彼此的键,而其余对象在其LinkedTo 属性中具有一个空数组应该匹配。

示例 C:

[
    "Id": 1,
    "Owner": 4
, 
    "Id": 2,
    "Owner": 4
, 
    "Id": 3,
    "Owner": 4
, 
    "Id": 4
]

在这里,如果其中三个对象的 Owner 属性的值与其余对象的 Id 属性匹配,则具有三个不同 Id 值的任何四个对象的集合都将匹配。

这可以通过BeEquivalentTo 以某种方式完成吗?

请注意,我确实想在应用任何断言之前遍历我的对象并添加实际的对象引用,因为该操作本身可能会引入其自身的错误。


编辑:根据要求,以下是上述示例的几个匹配和不匹配图表:

示例 A:

匹配:

[
    "Id": 5,
    "Name": "Foo",
    "NextId": -8
, 
    "Id": -8,
    "Name": "Bar"
]

匹配:

[
    "Id": 200,
    "Name": "Bar"
, 
    "Id": 30,
    "Name": "Foo",
    "NextId": 200
]

不匹配:

[
    "Id": 3,
    "Name": "Bar",
    "NextId": 6
, 
    "Id": 6,
    "Name": "Foo"
]

示例 B:

匹配:

[
    "Key": 20,
    "LinkedTo": [7]
, 
    "Key": 8,
    "LinkedTo": []
, 
    "Key": 7,
    "LinkedTo": [20]
]

匹配:

[
    "Key": 9,
    "LinkedTo": [100]
, 
    "Key": 100,
    "LinkedTo": [9]
, 
    "Key": 3,
    "LinkedTo": []
]

不匹配:

[
    "Key": 5,
    "LinkedTo": [10]
, 
    "Key": 10,
    "LinkedTo": []
, 
    "Key": 3,
    "LinkedTo": [5]
]

示例 C:

匹配:

[
    "Id": 1
, 
    "Id": 2,
    "Owner": 1
, 
    "Id": 3,
    "Owner": 1
, 
    "Id": 4,
    "Owner": 1
]

匹配:

[
    "Id": 10,
    "Owner": 20
, 
    "Id": 30,
    "Owner": 20
, 
    "Id": 20
, 
    "Id": 40,
    "Owner": 20
]

不匹配:

[
    "Id": 8,
    "Owner": 2
, 
    "Id": 12,
    "Owner": 8
, 
    "Id": 54,
    "Owner": 2
, 
    "Id": 2
]

【问题讨论】:

您想要断言您的数据究竟是什么? BeEquivalentTo 将断言集合具有相同数量的元素,并且A 中的每个元素在B 中具有匹配项,其中所有属性都具有等效值(递归地使用相同的等效逻辑)。测试是否在您期望它通过的地方失败?你能分享一个完整的代码测试吗? @JamesFaix:我不知道如何为此编写测试代码。我想断言第一个元素中的 NextId 与第二个元素中的 Id 具有相同的值——无论该值是 2、5 还是 369。我想等价逻辑会以某种方式包括之间的映射来自我预期数据的 ID 和在实际数据中找到的 ID。 BeEquivalentTo 最常用于断言 subject,例如针对预期值的函数返回值。您是否尝试断言有关您的代码的一些不变量?您能否提供一个应该匹配的主题+期望对,而另一对应该匹配失败? @JonasNyrup:完成。 (更详细地说:对于 expected 图的三个示例中的每一个,我添加了两个匹配的 subjects 和一个匹配失败的 subject .) 【参考方案1】:

据我了解您的问题,您有一个对象列表,并希望断言每个连续对的 NextIdId 匹配。

class MyClass

    public MyClass(int id, int nextId, string name)
    
        Id = id;
        NextId = nextId;
        Name = name;
    

    public int Id  get; set; 
    public int NextId  get; set; 
    public string Name  get; set; 

Fluent Assertions 不知道连续性,相反,我们可以创建两个列表,使得列表中的每个匹配对对应于原始列表中的连续项。 我们现在可以比较这两个列表,看看列表中的每一对是否具有所需的关系。

要比较NextIdId 的两个列表,我们需要指示Fluent Assertions 如何比较MyClass 的两个实例。

对于BeEquivalentTo,您可以使用Using<T>() + WhenTypeIs<T>() 组合指定如何与给定类型的实例进行比较。 请注意,默认情况下会尝试以任意顺序匹配这两个列表,但通过指定 WithStrictOrdering() 它将是成对比较。

[TestMethod]
public void Lists_BeEquivalentTo()

    var objects = new[]
    
        new MyClass(1, 2, "Foo"),
        new MyClass(2, 3, "Bar"),
        new MyClass(3, 4, "Baz")
    ;

    objects.Take(objects.Length - 1).Should().BeEquivalentTo(objects.Skip(1), opt => opt
        .WithStrictOrdering()
        .Using<MyClass>(e => e.Subject.NextId.Should().Be(e.Expectation.Id))
        .WhenTypeIs<MyClass>());

如果比较就像比较两个整数一样简单,也可以使用Equal() 断言,它只接受两个列表和一个指定两个元素何时相等的谓词。

[TestMethod]
public void Lists_Equal()

    var objects = new[]
    
        new MyClass(1, 2, "Foo"),
        new MyClass(2, 3, "Bar"),
        new MyClass(3, 4, "Baz")
    ;

    objects.Take(objects.Length - 1).Should().Equal(objects.Skip(1),
        (a, b) => a.NextId == b.Id);

【讨论】:

“并且想要断言每个连续对的 NextId 和 Id 匹配” - 不,抱歉,这根本不是我想要的。我希望比较两个对象图,其中的边不是由 .NET 对象引用表示,而是由匹配的 ID 值表示。我会尽量澄清我的问题。 我提供了一些更多示例性的对象图进行说明。

以上是关于如何比较通过 ID 链接的对象图?的主要内容,如果未能解决你的问题,请参考以下文章

流利的断言;结合集合和对象图比较断言

时序图怎么画

KS Gantt甘特图控件通过递归加载无限层级的数据

将对象图表示与邻接表和矩阵表示进行比较

Java中创建对象的内存图

如何在状态图中将代理的条件与所有连接的代理进行比较