FluentAssertions — сравнение объектов, содержащих свойства с похожими именами, но разными типами

У меня есть несколько классов в нашем коде, которые автоматически генерируются генератором XSD из определений файлов XSD. Классы выглядят очень похоже с похожими именами, но на основе схемы XSD (которую мы получили от внешнего поставщика) генерируемые классы относятся к разным типам. И это довольно сложные классы с множеством глубоко вложенных свойств и значений перечисления. Следовательно, мы привыкли работать с классами напрямую, потому что было сложно сделать обобщенный подход к работе с классами.

Но я принял вызов и (отчасти) преуспел. Чтобы избежать дублирования кода при работе с этими классами, я добавил свойства к классам, используя определения интерфейса вне сгенерированного файла XSD, чтобы предотвратить их перезапись при повторном создании классов, воспользовавшись преимуществом частичного объявления класса следующим образом:

Упрощенный пример сгенерированных XSD классов

public partial class xsdGeneratedClass1
{
    public xsdGeneratedClass1Header header { get; set }
    public xsdGeneratedClass1Body body { get; set; }
}

public partial class xsdGeneratedClass2
{
    public xsdGeneratedClass2Header header { get; set }
    public xsdGeneratedClass2Body body { get; set; }
}

Упрощенный пример интерфейса, в котором свойства также состоят из интерфейсов типов, которые мы написали так, чтобы они соответствовали свойствам в созданных XSD классах

public interface IXsdGeneratedClass
{
    IXsdGeneratedClassHeader header { get; set; }
    IXsdGeneratedClassBody body { get; set; }
}

Упрощенный пример реализации интерфейса за пределами сгенерированного XSD-файла

public partial class xsdGeneratedClass1 : IXsdGeneratedClass
{
    public IXsdGeneratedClassHeader header { get; set; }
    public IXsdGeneratedClassBody body { get; set; }
}

public partial class xsdGeneratedClass2 : IXsdGeneratedClass
{
    public IXsdGeneratedClassHeader header { get; set; }
    public IXsdGeneratedClassBody body { get; set; }
}

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

Проблема возникает при попытке сравнить объекты в наших модульных тестах с использованием Fluent Assertions. Кажется, что у Fluent Assertions есть проблемы со знанием того, какие свойства созданных объектов сравнивать. В этом простом примере экземпляр объекта xsdGeneratedClass1 будет иметь четыре свойства:

  1. общедоступный заголовок xsdGeneratedClass1Header {получить; набор }
  2. общественное тело xsdGeneratedClass1Body {получить; набор; }
  3. общедоступный заголовок IXsdGeneratedClassHeader { get; набор; }
  4. общественное тело IXsdGeneratedClassBody { get; набор; }

Объекты, которые я хочу сравнить, — это свойства заголовка и тела с типами интерфейса, поскольку они будут единственными, в которых есть фактические данные. Все свойства конкретного класса всегда нулевые. Итак, я сделал такие тесты:

class1.Should().BeEquivalentTo(expectedClass);

Но похоже, что Fluent Assertions постоянно сравнивает заголовок IXsdGeneratedClassHeader class1 с заголовком xsdGeneratedClass1Header ожидаемого класса, который равен нулю.

Я попытался использовать параметр RespectingRuntimeTypes, который делает тест пройденным, но тогда кажется, что он не сравнивает объекты так, как должен. Если я изменю значение свойства в свойстве заголовка ожидаемого класса, например, которое, как я знаю, не будет соответствовать значению в class1, тест все равно будет пройден.

Я пытался искать ответы в Интернете, и я подошел к концу в своих поисках, и в основном размышляю о том, должен ли я просто написать свой собственный инструмент или сделать бесчисленное количество утверждений вручную. Утверждения Оби-Вана, пожалуйста, помогите!


person Dennis Stuhr    schedule 19.09.2019    source источник
comment
Это может быть проблема, описанная в github.com/fluentassertions/fluentassertions/issues/1130. Также убедитесь, что общий тип T для BeEquivalentTo<T> равен IXsdGeneratedClass.   -  person Jonas Nyrup    schedule 21.09.2019
comment
Спасибо. Это решило мою проблему! Смотрите мой пост ниже :-)   -  person Dennis Stuhr    schedule 27.09.2019


Ответы (1)


Большое спасибо, Йонас Нюруп! Кажется, это дефект, который теперь исправлен в основной ветке Fluent Assertions. Тем временем кто-то опубликовал ответ в ветке github для обходного пути, который также сработал для меня. Очень счастлив. Спасибо!

Объявите где-нибудь этот класс, доступный для использования в ваших модульных тестах.

public class ReflectionMemberMatchingRule : IMemberMatchingRule
{
    public SelectedMemberInfo Match(SelectedMemberInfo expectedMember, object subject, string memberPath, IEquivalencyAssertionOptions config) => expectedMember;
}

Используйте класс расширения в своих модульных тестах, добавив конфигурацию в Fluent Assertions.

AssertionOptions.AssertEquivalencyUsing(x => x.Using(new ReflectionMemberMatchingRule()));

Теперь он работает так, как ожидалось!

person Dennis Stuhr    schedule 27.09.2019