Как сравнить графы объектов, которые связаны с помощью идентификаторов? - PullRequest
1 голос
/ 28 февраля 2020

Библиотека Fluent Assertions уделяет особое внимание возможностям сравнения графов объектов с помощью метода Should().BeEquivalentTo и связанных с ним методов. Как указывается в документации:

Should().BeEquivalentTo - очень мощная функция и одна из уникальных торговых точек Fluent Assertions.

Из прочтения примеров я У меня создается впечатление, что граф объектов должен быть связан с помощью ссылок на объекты.

Теперь у меня есть множество графов объектов и деревьев, содержимое которых я хотел бы проверить, но они обычно загружаются из файлов или из баз данных. Поэтому соединение существует исключительно по значениям свойств идентификатора, которые совпадают между объектами.

Например, представьте себе эту простую структуру (здесь написано в формате JSON) - пример A:

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

Я хотел бы использовать Fluent Assertions для проверки структуры такого графа объектов. То есть, независимо от того, какие значения поля Id и NextId действительно имеют, 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
}]

Здесь любой набор из четырех объектов с тремя различными значениями Id будет совпадать, если три из этих объектов имеют свойство Owner значение которого соответствует свойству 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
}]

1 Ответ

1 голос
/ 28 февраля 2020

Как я понимаю ваш вопрос, у вас есть список объектов, и вы хотите утверждать, что NextId и Id каждой последовательной пары совпадают.

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; }
}

Свободные утверждения не знают о последовательности, вместо этого мы можем создать два списка так, чтобы каждая совпадающая пара в списках соответствовала последовательным элементам в исходном списке. Теперь мы можем сравнить два списка, чтобы увидеть, имеет ли каждая пара в списках желаемое отношение.

Чтобы сравнить два списка по NextId и Id, нам нужно дать инструкции Свободным утверждениям, как сравнивать два экземпляра. из 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);
}
...