12

Hemos escrito una prueba que se parece a la siguiente. Esta prueba requiere que hemos creado en Equal -sobrecarga para la CodeTableItem -class:Aplicación de [AutoFixture] Semántica Comparación de Likeness a secuencias/colecciones/matrices/IEnumerable

ICollection<CodeTableItem> expectedValutaList = new List<CodeTableItem>(); 
expectedValutaList.Add(new CodeTableItem("DKK", "DKK")); 
expectedValutaList.Add(new CodeTableItem("EUR", "EUR")); 
RepoDac target = new RepoDac(); 

var actual = target.GetValutaKd(); 

CollectionAssert.AreEqual(expectedValutaList.ToList(),actual.ToList()); 

La prueba funciona bien, pero tiene la desafortunada dependencia a la Equality -función, es decir, si extiendo la -class CodeTableItem con uno más campo, y se olvida de extender la función Equals, la prueba de la unidad todavía se ejecuta en verde, aunque no probamos todos los campos. Queremos evitar esta contaminación Equality (consulte Test Specific Equality), que se ha escrito solo para cumplir con la prueba.

Hemos intentado usar OfLikeness, y han reescrito la prueba de esta manera:

ICollection<CodeTableItem> expectedValutaList = new List<CodeTableItem>(); 
expectedValutaList.Add(new CodeTableItem("DKK", "DKK")); 
expectedValutaList.Add(new CodeTableItem("EUR", "EUR")); 
var expectedValutaListWithLikeness = 
      expectedValutaList.AsSource().OfLikeness<List<CodeTableItem>>(); 

RepoDac target = new RepoDac(); 
ICollection<CodeTableItem> actual; 

actual = target.GetValutaKd(); 

expectedValutaListWithLikeness.ShouldEqual(actual.ToList()); 

Pero la prueba falla porque el Capacity no es igual. He escrito código que se ejecuta a través de la reflexión muchas veces, y generalmente terminaba implementando sobrecargas para ignorar campos. ¿Hay alguna manera de ignorar ciertos campos con OfLikeness o ShouldEqual? ¿O hay alguna otra forma de resolver este problema?

Respuesta

8

Solo agregue el .Without(x => x.Capacity) y la instancia de Me gusta ignorará la propiedad Capacity al comparar valores.

var expectedValutaListWithLikeness = 
     expectedValutaList.AsSource().OfLikeness<List<CodeTableItem>>() 
     .Without(x => x.Capacity); 

Actualización:

Como señala Marcos Seemann en su respuesta, lo que probablemente quiere es comparar cada elemento uno contra el otro. Aquí hay una manera ligeramente diferente que le permite realizar muy comparaciones flexibles.

Suponiendo que la clase RepoDac vuelve algo como:

public class RepoDac 
{ 
    public ICollection<CodeTableItem> GetValutaKd() 
    { 
     return new[] 
     { 
      new CodeTableItem("DKK", "DKK"), 
      new CodeTableItem("EUR", "EUR") 
     }; 
    } 
} 

Para cada instancia en el expectedValutaList puede crear un proxy dinámico que prevalece es igual usando Semejanza:

var object1 = new CodeTableItem("DKK", "DKK1") 
    .AsSource().OfLikeness<CodeTableItem>() 
    .Without(x => x.Property2) 
    .CreateProxy(); 

var object2 = new CodeTableItem("EUR2", "EUR") 
    .AsSource().OfLikeness<CodeTableItem>() 
    .Without(x => x.Property1) 
    .CreateProxy(); 

Aviso cómo el object1 y object2 tienen incluso diferentes Equals generados dinámicamente. (La primera ignora Propiedad2 mientras que el segundo ignora Propiedad1.)

La prueba a continuación pasa:

var expected = new List<CodeTableItem>(); 
expected.Add(object1); 
expected.Add(object2); 

var target = new RepoDac(); 
var actual = target.GetValutaKd(); 

Assert.IsTrue(expected.SequenceEqual(actual)); 

Nota:

Se requiere para comenzar con el expected instancia que contiene los proxies generados dinámicamente (anulando Iguales).

Puede encontrar más información en esta característica here.

12

¿Por qué usted no desea hacerlo así

No creo que la creación de una semejanza de cualquier List<T> hace lo que quiere que haga. Según tengo entendido, desea comparar el contenido de dos listas. Eso no es lo mismo que comparar dos listas ...

Considere lo que hace la semejanza: compara propiedad valores. ¿Cuáles son las propiedades de List<T>?

Son

  • Capacidad
  • Conde

a Nikos Baxevanis señala en su respuesta, puede utilizar la Sin método para ignorar el valor de la propiedad de la capacidad, pero que los medios que solo queda la propiedad Count.

En otras palabras, si lo hizo, esto:

expectedValutaListWithLikeness.ShouldEqual(actual.ToList()); 

sería funcionalmente equivalente a esto:

Assert.AreEqual(expected.Count, actual.Count) 

En otras palabras, las listas pueden tener totalmente diferentes de datos, pero el la prueba aún pasaría si solo cada lista tiene la misma cantidad de elementos. Esto probablemente no es lo que quiere ...

Lo que debe hacer

Usted puede usar la imagen para comparar cada elemento uno contra el otro. Algo como esto debería funcionar:

var expectedValutaList = new List<CodeTableItem>(); 
expectedValutaList.Add(new CodeTableItem("DKK", "DKK")); 
expectedValutaList.Add(new CodeTableItem("EUR", "EUR")); 

var expectedValutaListWithLikeness = from cti in expectedValutaList 
            select cti 
             .AsSource() 
             .OfLikeness<CodeTableItem>(); 

var target = new RepoDac(); 

var actual = target.GetValutaKd(); 

Assert.IsTrue(expectedValutaListWithLikeness.Cast<object>().SequenceEqual(
    actual.Cast<object>())); 

También puede ser capaz de utilizar CollectionAssert para la afirmación, pero ha sido muchos años desde la última vez que utilicé MSTest No puedo recordar las peculiaridades de ese método ...

+0

Muchas gracias, Mark, ¡eso fue todo! ¡Elegante! –

3

La siguiente respuesta emanaba de me asking myself a duplicate of this question, véase más adelante

Se podría utilizar una operación SequenceLike que alude al operador de LINQ SequenceEqual.

Esto permite escribir: -

[Theory, AutoData] 
public void ShouldMap( Dto inputDto) 
{ 
    var mapped = inputDto.ToModel(); 

    inputDto.AsSource().OfLikeness<Model>() 
     .Without(x => x.IgnorableProperty) 
     .With(x => x.Tags).EqualsWhen((dto, model) => 
      model.Tags.SequenceLike(dto.Tags)) 
     .ShouldEqual(mapped); 
} 

brillante aplicación corta de todo-en-uno basado en ayudante de Seemann @ Marcos respuesta gracias a que llevó a @Nikos Baxevanis: -

static class LikenessSequenceExtensions 
{ 
    public static bool SequenceLike<T, TSource>(this IEnumerable<T> that, IEnumerable<TSource> source) 
    { 
     return SequenceLike<T, TSource>(that, source, x => x); 
    } 

    public static bool SequenceLike<T, TSource>(this IEnumerable<T> that, IEnumerable<TSource> source, Func<Likeness<TSource, T>, IEquatable<T>> customizeLikeness) 
    { 
     return source.Select(x => customizeLikeness(x.AsSource().OfLikeness<T>())).SequenceEqual(that.Cast<object>()); 
    } 
} 

Mi implementación original:

static class LikenessSequenceExtensions0 
{ 
    public static bool SequenceLike0<T, TSource>(this T[] that, TSource[] source) 
    { 
     return source.SequenceLike0(that, likeness => likeness); 
    } 

    public static bool SequenceLike0<T, TSource>(this T[] that, TSource[] source, Func<Likeness<TSource, T>, IEquatable<T>> customizeLikeness) 
    { 
     return source.SequenceEqual(that, (x, y) => customizeLikeness(x.AsSource().OfLikeness<T>()).Equals(y)); 
    } 

    public static bool SequenceEqual<T, TSource>(this T[] that, TSource[] source, Func<T, TSource, bool> equals) 
    { 
     return that.Length == source.Length && that.Zip(source, Tuple.Create).All(x => equals(x.Item1, x.Item2)); 
    } 
} 

original pregunta duplicado

Busco la manera más limpia para gestionar Test Specific Equality para matrices/IEnumerable<T>/seq<'T> en mis pruebas basadas en xunit.AutoFixture.

OOTB (he perdido la referencia de dónde aprendí esto), las versiones Ploeh.SemanticComparisonLikeness hasta 2.12 funcionan solo en elementos individuales.

¿Cuál es la mejor manera de aplicar las mismas técnicas a colecciones de artículos (idealmente OOTB pero muy abierto a un conjunto de métodos de extensión bien evaluados) para facilitar la expresión de elementos que incluyen objetos incrustados de forma composable?

Esto realmente es una auto-respuesta, así que puedo esconder los ayudantes y permitir un lugar para poner un "hazlo de esta manera en V nn" debería ofrecerse en el futuro, pero no sería la primera vez que me sorprendió la sutileza de la respuesta posible de Aflicted AFicionados

+0

¡Gran código ... gracias! – AxelEckenberger

3

Quería que esto sea explícito para los demás que tengan este problema, utilizando el segundo ejemplo de código de Rubén, donde desea comparar el Calendario y el Calendario. Vacaciones, al personalizar la comparación de ambos:

var expectedCalendar = newCalendar.AsSource() 
    .OfLikeness<Calendar>() 
    .Without(c=>c.Id) //guid, will never be equal 
    .With(c=>c.Holidays).EqualsWhen((source, dest) => 
     source.Holidays.SequenceLike(dest.Holidays, holiday => 
      holiday.Without(h=>h.SecondsUntil) //changes every second 
    )); 

En este ejemplo, primero configura propiedades para excluir, etc. en el objeto Calendario. Luego, le da una implementación EqualsWith personalizada para manejar la colección Holidays. El día festivo => lambda le permite personalizar la comparación del niño, al igual que el padre. Puedes seguir anidando siempre y cuando disfrutes de muchos paréntesis.

+0

+1 Agradable. Y oye, esos amistosos abrazos entre paréntesis son muy reconfortantes :) –