c# - periodos - ¿Cómo afirmar que dos listas contienen elementos con las mismas propiedades públicas en NUnit?
radio atomico (9)
¿Has probado algo como esto?
Assert.That(foundCollection, Is.EquivalentTo(expectedCollection))
Quiero afirmar que los elementos de dos listas contienen valores que esperaba, algo así como:
var foundCollection = fooManager.LoadFoo();
var expectedCollection = new List<Foo>()
{
new Foo() { Bar = "a", Bar2 = "b" },
new Foo() { Bar = "c", Bar2 = "d" }
};
//assert: I use AreEquivalent since the order does not matter
CollectionAssert.AreEquivalent(expectedCollection, foundCollection);
Sin embargo, el código anterior no funcionará (supongo que porque .Equals () no devuelve verdadero para diferentes objetos con el mismo valor). En mi prueba, solo me importan los valores de propiedad pública, no si los objetos son iguales. ¿Qué puedo hacer para hacer mi afirmación?
Código simple que explica cómo usar el IComparer
using System.Collections;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace CollectionAssert
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
IComparer collectionComparer = new CollectionComparer();
var expected = new List<SomeModel>{ new SomeModel { Name = "SomeOne", Age = 40}, new SomeModel{Name="SomeOther", Age = 50}};
var actual = new List<SomeModel> { new SomeModel { Name = "SomeOne", Age = 40 }, new SomeModel { Name = "SomeOther", Age = 50 } };
NUnit.Framework.CollectionAssert.AreEqual(expected, actual, collectionComparer);
}
}
public class SomeModel
{
public string Name { get; set; }
public int Age { get; set; }
}
public class CollectionComparer : IComparer, IComparer<SomeModel>
{
public int Compare(SomeModel x, SomeModel y)
{
if(x == null || y == null) return -1;
return x.Age == y.Age && x.Name == y.Name ? 0 : -1;
}
public int Compare(object x, object y)
{
var modelX = x as SomeModel;
var modelY = y as SomeModel;
return Compare(modelX, modelY);
}
}
}
El uso de Has.All.Matches () funciona muy bien para comparar una colección encontrada con la colección esperada . Sin embargo, no es necesario definir el predicado utilizado por Has.All.Matches () como una función separada. Para comparaciones relativamente simples, el predicado se puede incluir como parte de la expresión lambda de esta manera.
Assert.That(found, Has.All.Matches<Foo>(f =>
expected.Any(e =>
f.Bar1 == e.Bar1 &&
f.Bar2 == e.Bar2 &&
f.Bar3= = e.Bar3)));
Ahora, si bien esta afirmación asegurará que cada entrada en la colección encontrada también exista en la colección esperada , no demuestra lo contrario, es decir, que cada entrada en la colección esperada está contenida en la colección encontrada . Entonces, cuando es importante saber que el contenido encontrado y esperado contiene semánticamente equivalentes (es decir, contienen las mismas entradas semánticamente equivalentes), debemos agregar una aseveración adicional.
La opción más sencilla es agregar lo siguiente.
Assert.AreEqual(found.Count() == expected.Count());
Para aquellos que prefieren un martillo más grande, la siguiente afirmación podría ser usada en su lugar.
Assert.That(expected, Has.All.Matches<Foo>(e =>
found.Any(f =>
e.Bar1 == f.Bar1 &&
e.Bar2 == f.Bar2 &&
e.Bar3= = f.Bar3)));
Al utilizar la primera afirmación anterior junto con la segunda afirmación (preferida) o la tercera, ahora hemos demostrado que las dos colecciones son semánticamente iguales.
No, NUnit no tiene tal mecanismo a partir del estado actual. Tendrás que lanzar tu propia lógica de aserción. Ya sea como método separado o utilizando Has.All.Matches
:
Assert.That(found, Has.All.Matches<Foo>(f => IsInExpected(f, expected)));
private bool IsInExpected(Foo item, IEnumerable<Foo> expected)
{
var matchedItem = expected.FirstOrDefault(f =>
f.Bar1 == item.Bar1 &&
f.Bar2 == item.Bar2 &&
f.Bar3 == item.Bar3
);
return matchedItem != null;
}
Por supuesto, esto supone que conoce todas las propiedades relevantes por adelantado (de lo contrario, IsInExpected
tendrá que recurrir a la reflexión) y que el orden de los elementos no es relevante.
(Y su suposición fue correcta, la colección de NUnit afirma que utiliza comparadores predeterminados para los tipos, que en la mayoría de los casos definidos por el usuario serán ReferenceEquals
del objeto)
Para realizar operaciones de equivalencia en tipos complejos, necesita implementar IComaprable.
http://support.microsoft.com/kb/320727
Alternativamente, puedes usar la reflexión recursiva, que es menos deseable.
Recomiendo contra el uso de la reflexión o cualquier cosa compleja, simplemente agrega más trabajo / mantenimiento.
Serializar el objeto (recomiendo json) y compararlos en cadena. No estoy seguro de por qué se opone al pedido, pero aún así lo recomendaría ya que guardará una comparación personalizada para cada tipo.
Y funciona automáticamente con el cambio de objetos de dominio.
Ejemplo (SharpTestsEx para fluidez)
using Newtonsoft.Json;
using SharpTestsEx;
JsonConvert.SerializeObject(actual).Should().Be.EqualTo(JsonConvert.SerializeObject(expected));
Puede escribirlo como una simple extensión y hacerlo más legible.
public static class CollectionAssertExtensions
{
public static void CollectionAreEqual<T>(this IEnumerable<T> actual, IEnumerable<T> expected)
{
JsonConvert.SerializeObject(actual).Should().Be.EqualTo(JsonConvert.SerializeObject(expected));
}
}
y luego usando tu ejemplo llámalo así:
var foundCollection = fooManager.LoadFoo();
var expectedCollection = new List<Foo>()
{
new Foo() { Bar = "a", Bar2 = "b" },
new Foo() { Bar = "c", Bar2 = "d" }
};
foundCollection.CollectionAreEqual(foundCollection);
Recibirás un mensaje de afirmación como el siguiente:
...: "a", "Bar2": "b"}, {"Bar": "d", "Bar2": "d"}]
...: "a", "Bar2": "b"}, {"Bar": "c", "Bar2": "d"}]
... _ __ _ __ _ __ _ __ _ __ _ __ ^ _ __ _ _
Tuve un problema similar. Listado de colaboradores, que contiene "comentaristas" y otras personas ... Quiero obtener todos los comentarios y de eso derivan los creadores, pero a menudo solo estoy interesado en creadores únicos. Si alguien creó 50 comentarios, solo quiero que su nombre aparezca una vez. Así que escribo una prueba para ver que los comentaristas están en el resultado GetContributors ().
Puede que esté equivocado, pero lo que pienso después de (lo que busqué después de encontrar esta publicación) es afirmar que hay exactamente uno de cada elemento en una colección, que se encuentra en otra colección.
Resolví esto así:
Assert.IsTrue(commenters.All(c => actual.Count(p => p.Id == c.Id) == 1));
Si también desea que la lista resultante no contenga otros elementos de lo esperado, también podría comparar la longitud de las listas ...
Assert.IsTrue(commenters.length == actual.Count());
Espero que esto sea de utilidad. Si es así, le agradecería que calificara mi respuesta.
Una opción es escribir restricciones personalizadas para comparar los elementos. Aquí hay un buen artículo sobre el tema: http://www.davidarno.org/2012/07/25/improving-nunit-custom-constraints-with-syntax-helpers/
RESPUESTA REWORKED
Existe una CollectionAssert.AreEqual(IEnumerable, IEnumerable, IComparer)
para afirmar que dos colecciones contienen los mismos objetos en el mismo orden, utilizando una implementación de IComparer
para verificar la equivalencia del objeto.
En el escenario descrito anteriormente, el orden no es importante. Sin embargo, para manejar suficientemente la situación en la que hay múltiples objetos equivalentes en las dos colecciones, se hace necesario ordenar primero los objetos en cada colección y usar la comparación uno por uno para asegurar que también el número de objetos equivalentes sea el mismo. en las dos colecciones.
Enumerable.OrderBy
proporciona una sobrecarga que toma un IComparer<T>
. Para asegurarse de que las dos colecciones se ordenan en el mismo orden, es más o menos necesario que los tipos de propiedades de identificación se implementen en IComparable
. Aquí hay un ejemplo de una clase comparadora que implementa las IComparer
e IComparer<Foo>
, y donde se supone que Bar
tiene prioridad al ordenar:
public class FooComparer : IComparer, IComparer<Foo>
{
public int Compare(object x, object y)
{
var lhs = x as Foo;
var rhs = y as Foo;
if (lhs == null || rhs == null) throw new InvalidOperationException();
return Compare(lhs, rhs);
}
public int Compare(Foo x, Foo y)
{
int temp;
return (temp = x.Bar.CompareTo(y.Bar)) != 0 ? temp : x.Bar2.CompareTo(y.Bar2);
}
}
Para afirmar que los objetos en las dos colecciones son iguales y vienen en números iguales (pero no necesariamente en el mismo orden para comenzar), las siguientes líneas deben hacer el truco:
var comparer = new FooComparer();
CollectionAssert.AreEqual(
expectedCollection.OrderBy(foo => foo, comparer),
foundCollection.OrderBy(foo => foo, comparer), comparer);