Как сравнить два анонимных типа или две коллекции разных типов с помощью SemanticComparison

1. Есть ли простой способ сравнить два анонимных типа с помощью SemanticComparison от AutoFixture? Моя текущая проблема заключается в том, что я не могу создать подобие для второго анонимного объекта. Упрощенный пример:

var srcAnon = new { time = expectedTime, data = docsArray };
var resultAnon = new { time=actualTime, data = sutResponseArray };

var expectedAlike = srcAnon.AsSource()
            .OfLikeness<??WhatsHere??>()

2. Я думаю, что этот вопрос очень связан с первым, поскольку они оба используют SemanticComparison для создания IEquatable реализаций. В этом вопросе Марк Симанн дал ответ о том, как сделать это с помощью утверждений MSTest и метода LINQ SequenceEqual.

Возможно ли использовать библиотеку утверждений XUnit2 в аналогичном сценарии? XUnit поддерживает Assert.Equal() для коллекций одного типа, можно ли его использовать для коллекций разных типов, но если элементы реализуют IEquatable (используя Likeness). Что-то вроде этого (это не работает, так как result и allLikeness имеют разные типы):

Assert.Equal(allLikeness.ToArray(),result.ToArray());

person Alexey Shcherbak    schedule 30.09.2015    source источник
comment
связанные: stackoverflow.com/a/16714559/11635 - поскольку анонимный тип "невыразим", вам нужно используйте общий метод, чтобы преодолеть это.   -  person Ruben Bartelink    schedule 30.09.2015
comment
Два ключевых момента: 1. Анонимные методы уже встроены в равенство, поэтому если вы можете сгенерировать анонимную каноническую характеристику, выбрав соответствующие биты из фактической и опираясь на это, вы должны просто иметь возможность использовать утверждения коллекции v2 без с помощью SemanticComparison 2. Существуют различные способы создания сходства, подробно описанные здесь . Есть также некоторые работы, которые IIRC датирует задним числом, для которых след начинается здесь   -  person Ruben Bartelink    schedule 30.09.2015
comment
связанные: stackoverflow.com/questions/16716161   -  person Ruben Bartelink    schedule 30.09.2015


Ответы (1)


Независимо от какой-либо среды модульного тестирования, вы всегда можете перейти к перегрузке SequenceEquals<object>, которая также требует компаратор. Это позволит вам сравнивать совершенно разные списки. Этот тест демонстрирует, как можно «обмануть» .NET, заставив его «думать», что два разнородных массива идентичны:

[Fact]
public void TestDisparateSequences()
{
    var ints = new[] { 1, 3, 5, 7 };
    var strings = new[] { "foo", "bar", "baz", "qux" };

    Assert.True(
        ints.Cast<object>().SequenceEqual(
            strings.Cast<object>(),
            new TrueComparer<object>()),
        "Arrays look like they are equal.");
}

private class TrueComparer<T> : IEqualityComparer<T>
{
    public bool Equals(T x, T y)
    {
        return true;
    }

    public int GetHashCode(T obj)
    {
        return 0;
    }
}

Этот тест проходит, потому что TrueComparer всегда возвращает true.

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

SemantiComparison предоставляет класс SemanticComparer<T>, реализующий IEqualityComparer<T>, но он работает только со значениями одного типа. Таким образом, чтобы использовать его для сравнения разнородных последовательностей, вам нужно отобразить один из списков в последовательность другого типа.

Часто у вас уже есть такая карта, но если нет, то это хорошая мотивация для ее создания. В противном случае вы можете использовать средство семантического сопоставления, например AutoMapper.

Предположим, например, что у вас есть такой класс Foo:

public class Foo
{
    public int Number { get; set; }

    public string Text { get; set; }
}

и еще один класс Bar, очень похожий:

public class Bar
{
    public int Number { get; set; }

    public string Text { get; set; }
}

Теперь вы можете сравнивать foos и bar с помощью карты и SemanticComparison<Bar>:

[Fact]
public void TestEquivalentSequences()
{
    var foos = new[]
    {
        new Foo { Number = 42, Text = "ploeh" },
        new Foo { Number = 1337, Text = "fnaah" }
    };
    var bars = new[]
    {
        new Bar { Number = 42, Text = "ploeh" },
        new Bar { Number = 1337, Text = "fnaah" }
    };

    AutoMapper.Mapper.CreateMap<Foo, Bar>();

    Assert.True(
        foos.Select(AutoMapper.Mapper.Map<Bar>).SequenceEqual(
            bars,
            new SemanticComparer<Bar>()),
        "Mapped arrays should be equivalent.");
}

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

person Mark Seemann    schedule 02.10.2015
comment
(Особенно для тестов) Вы можете использовать Mapper.DynamicMap вместо Map, чтобы избежать CreateMap. Если вы считаете, что это делает его лучше, и отредактируете это, я удалю этот комментарий. (Например, наличие CreateMap является более общим, поскольку можно сразу же настроить сопоставление, если оно предназначено в качестве начальной вставки из интернет-кода... Также опечатка: SemantiComparer - person Ruben Bartelink; 02.10.2015