Verifying complete Mapping of an [unordered] Colle

2019-01-29 05:08发布

问题:

I'm using xUnit.net, AutoFixture and SemanticComparison and want to verify the results of a mapping.

On the individual item level, I'm well covered.

Given

  • The items share an identifying key
  • I want to do a comparison on the value elements on both side
  • I don't care about ordering (and don't want my Assertion to break under re-ordering)

How do I verify that each and every input item maps to one and only one output item in a DAMP yet DRY manner using as much OOTB componentry as possible ?

Fixtures:

class Input
{ 
   public string Name, Description;
}

class Output
{ 
   public string Name, Description, IgnoreThisField;
}

Skeleton Test:

[Theory,AutoData]
void MappingWorks( Mapper sut, Input[] inputs)
{
    var outputs = sut.Map( inputs);

    // TODO assert that every input is mapped
    // TODO assert that we have have no extra outputs
}

回答1:

Given some AssertResemblance.Like Collection helpers[1], you

  1. Put them in the same order (which is most cleanly done in the test body as the input and output collections will be of different types and unless SemanticComparison grows a feature I don't think it can be usefully generalised)
  2. Let Ploeh.SemanticComparison's Likeness do the matching on Name
  3. Let xUnit.net V2's Assert.Collection do the correlation (it doesn't yet give a greate message)

Final result is:

var results = from result in outputs orderby result.Name select result;

var expectations = from input in inputs orderby input.Name select input;
AssertResemblance.Like( expectations, results, 
   configure=>configure
      .Without(x=>x.IgnoreThisField) );

[1]

static class AssertResemblance
{
    public static void Like<T, T2>( IEnumerable<T> expected, IEnumerable<T2> actual )
    {
        Like<T, T2>( expected, actual, x=>x );
    }

    public static void Like<T, T2>( IEnumerable<T> expected, IEnumerable<T2> actual, Func<Likeness<T, T2>, Likeness<T, T2>> configureLikeness )
    {
        Assert2.Collection( actual, SelectShouldEqualAsAction( expected.Select( x => configureLikeness( x.AsSource().OfLikeness<T2>() ) ) ) );
    }

    public static void Like<T>( IEnumerable<T> expected, IEnumerable<T> actual, Func<IEnumerable<T>, IEnumerable<T>> unify )
    {
        Like<T>( expected, actual, unify, x=>x );
    }

    public static void Like<T>( IEnumerable<T> expected, IEnumerable<T> actual, Func<IEnumerable<T>,IEnumerable<T>> unify, Func<Likeness<T, T>, Likeness<T, T>> configureLikeness )
    {
        Assert2.Collection( unify( actual ), SelectShouldEqualAsAction( unify(expected).Select( x => configureLikeness( x.AsSource().OfLikeness<T>() ) ) ) );
    }

    static Action<TDestination>[] SelectShouldEqualAsAction<TSource, TDestination>( IEnumerable<Likeness<TSource, TDestination>> that )
    {
        return (from it in that select (Action<TDestination>)it.ShouldEqual).ToArray();
    }
}


回答2:

Given a [very neat] FullOuterJoin operation and xUnit.net V2, you can express it as:

static void VerifyFeaturesetFullyMapped(
    IEnumerable<Output> outputs,
    IEnumerable<Input> inputs )
{
    Assert.All(
        inputs.FullOuterJoin( outputs,
            f => f.Item1, r => r.Name,
            ( x, y, key ) => new { 
                InDescription = x.Item2, 
                OutDescription = y.Description } ),
        inout =>
            Assert.Equal( inout.InDescription, inout.OutDescription ) );
}