2

1. Is there a simple way to compare two anonymous types using SemanticComparison from AutoFixture ? My current issue is that I can't construct Likeness for second anonymous object. Simplified example :

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

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

2. I think this question is pretty related to the first one as they both use SemanticComparison to create IEquatable implementations. In this question Mark Seemann provided an answer on how to do it using MSTest assertions and LINQ SequenceEqual method.

Is this possible to use XUnit2 assertions library in the similar scenario? XUnit supports Assert.Equal() for collections of the same type, can it be used for collections of different types, but if the elements implement IEquatable (using Likeness). Something like this (this doesn't work as result and allLikeness have different types):

Assert.Equal(allLikeness.ToArray(),result.ToArray());
Community
  • 1
  • 1
Alexey Shcherbak
  • 3,394
  • 2
  • 27
  • 44
  • 2
    related: http://stackoverflow.com/a/16714559/11635 - because the anonymous type is 'unspeakable', you'll need to use a generic method to get over that. – Ruben Bartelink Sep 30 '15 at 09:09
  • 3
    Two key points: 1. Anonymous methods already have built in equality so you so if you can generate an anonymous canonicalized characterization by selecting out the relevant bits from the actual and lean on that, you should just be able to use the v2 collection asserts without using SemanticComparison 2. There are various ways of [generating resemblances, described well here](http://blog.ploeh.dk/2012/06/22/ResemblanceandLikeness/). There's also some work that IIRC postdates that for which [the trail begins here](https://github.com/AutoFixture/AutoFixture/issues/99) – Ruben Bartelink Sep 30 '15 at 09:25
  • 2
    related: http://stackoverflow.com/questions/16716161 – Ruben Bartelink Sep 30 '15 at 09:25

1 Answers1

4

Independently of any unit testing framework, you can always drop down to the SequenceEquals<object> overload that also takes a comparer. This will enable you to compare completely disparate lists. This test demonstrates how you can 'trick' .NET into 'thinking' that two heterogeneous arrays are identical:

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

This test passes, because TrueComparer always returns true.

Obviously, that's not particularly practical, but it points out the relevant building blocks one can use to compare heterogeneous sequences.

SemantiComparison provides a SemanticComparer<T> class that implements IEqualityComparer<T>, but it only works on values of the same type. Thus, in order to use it to compare heterogeneous sequences, you'll need to map one of the lists into a sequence of the other type.

Often, you'll already have such a map lying around, but if not, it's a good motivation for building one. Otherwise, you can use use a semantic mapper like AutoMapper.

Assume, as an example, that you have a Foo class like this:

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

    public string Text { get; set; }
}

and another Bar class, very similar:

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

    public string Text { get; set; }
}

You can now compare foos and bars using a map and 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.");
}

Still, it will make your life a whole lot easier if you give your objects Structural Equality. This answer only sketches out what's possible, not what's recommended.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • 1
    (Esp for tests) You can use `Mapper.DynamicMap` instead of `Map` to avoid the `CreateMap`. If you think it makes it better and edit that in, I'll delete this comment. (Obv having a `CreateMap` is more general in that one can immediately go customize the mapping if this intended as starter Paste from the Internet code... Also typo: SemantiComparer – Ruben Bartelink Oct 02 '15 at 18:18