Compare la igualdad entre dos objetos en NUnit

126

Estoy tratando de afirmar que un objeto es "igual" a otro objeto.

Los objetos son solo instancias de una clase con un conjunto de propiedades públicas. ¿Hay una manera fácil de hacer que NUnit afirme la igualdad en función de las propiedades?

Esta es mi solución actual, pero creo que puede haber algo mejor:

Assert.AreEqual(LeftObject.Property1, RightObject.Property1)
Assert.AreEqual(LeftObject.Property2, RightObject.Property2)
Assert.AreEqual(LeftObject.Property3, RightObject.Property3)
...
Assert.AreEqual(LeftObject.PropertyN, RightObject.PropertyN)

Lo que voy a buscar estaría en el mismo espíritu que la CollectionEquivalentConstraint en la que NUnit verifica que el contenido de dos colecciones sea idéntico.

Michael Haren
fuente

Respuestas:

51

Anule .Equals para su objeto y en la prueba de la unidad, simplemente puede hacer esto:

Assert.AreEqual(LeftObject, RightObject);

Por supuesto, esto podría significar que simplemente mueve todas las comparaciones individuales al método .Equals, pero le permitiría reutilizar esa implementación para múltiples pruebas, y probablemente tenga sentido si los objetos deberían poder compararse con los hermanos de todos modos.

Lasse V. Karlsen
fuente
2
Gracias, Lassevk. ¡Esto funcionó para mí! Implementé .Equals de acuerdo con las pautas aquí: msdn.microsoft.com/en-us/library/336aedhh(VS.80).aspx
Michael Haren
12
Y GetHashCode (), obviamente ;-p
Marc Gravell
El número 1 en la lista de esa página es anular GetHashCode, y dijo que siguió esas pautas :) Pero sí, un error común al ignorar eso. Por lo general, no es un error que notarás la mayor parte del tiempo, pero cuando lo haces, es como una de esas veces que dices "Oh, oye, ¿por qué esta serpiente me arranca los pantalones y por qué me está mordiendo el culo"?
Lasse V. Karlsen
1
Una advertencia importante: si su objeto también se implementa IEnumerable, se comparará como una colección, independientemente de las implementaciones Equalsanuladas porque NUnit le da IEnumerablemayor prioridad. Vea los NUnitEqualityComparer.AreEqualmétodos para más detalles. Puede anular el comparador utilizando uno de los Using()métodos de restricción de igualdad . Incluso entonces, no es suficiente implementar el no genérico IEqualityComparerdebido al adaptador que utiliza NUnit.
Kaleb Pederson el
13
Más advertencia: la implementación GetHashCode()en tipos mutables se comportará mal si alguna vez usa ese objeto como clave. En mi humilde opinión, anulando Equals(), GetHashCode()y haciendo que el objeto inmutable sólo para probar que no tiene sentido.
bavaza
118

Si no puede anular Equals por algún motivo, puede crear un método auxiliar que recorra en iteración las propiedades públicas mediante reflexión y afirme cada propiedad. Algo como esto:

public static class AssertEx
{
    public static void PropertyValuesAreEquals(object actual, object expected)
    {
        PropertyInfo[] properties = expected.GetType().GetProperties();
        foreach (PropertyInfo property in properties)
        {
            object expectedValue = property.GetValue(expected, null);
            object actualValue = property.GetValue(actual, null);

            if (actualValue is IList)
                AssertListsAreEquals(property, (IList)actualValue, (IList)expectedValue);
            else if (!Equals(expectedValue, actualValue))
                Assert.Fail("Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expectedValue, actualValue);
        }
    }

    private static void AssertListsAreEquals(PropertyInfo property, IList actualList, IList expectedList)
    {
        if (actualList.Count != expectedList.Count)
            Assert.Fail("Property {0}.{1} does not match. Expected IList containing {2} elements but was IList containing {3} elements", property.PropertyType.Name, property.Name, expectedList.Count, actualList.Count);

        for (int i = 0; i < actualList.Count; i++)
            if (!Equals(actualList[i], expectedList[i]))
                Assert.Fail("Property {0}.{1} does not match. Expected IList with element {1} equals to {2} but was IList with element {1} equals to {3}", property.PropertyType.Name, property.Name, expectedList[i], actualList[i]);
    }
}
Juanma
fuente
@wesley: esto no es cierto. Método Type.GetProperties: devuelve todas las propiedades públicas del tipo actual. Ver msdn.microsoft.com/en-us/library/aky14axb.aspx
Sergii Volchkov
44
Gracias. sin embargo, tuve que cambiar el orden de los parámetros reales y esperados ya que la conversión es que lo esperado es un parámetro antes que real.
Valamas
este es un mejor enfoque en mi humilde opinión, las anulaciones de Equal & HashCode no deberían basarse en la comparación de cada campo y, además, es muy tedioso hacer en todos los objetos. ¡Buen trabajo!
Scott White
3
Esto funciona muy bien si su tipo solo tiene tipos básicos como propiedades. Sin embargo, si su tipo tiene propiedades con tipos personalizados (que no implementan Equals) fallará.
Bobby Cannon
Se agregó cierta recursividad para las propiedades del objeto, pero tuve que omitir las propiedades indexadas:
cerhart
113

No anule Equals solo con fines de prueba. Es tedioso y afecta la lógica del dominio. En lugar,

Use JSON para comparar los datos del objeto.

No hay lógica adicional en sus objetos. No hay tareas adicionales para probar.

Solo usa este método simple:

public static void AreEqualByJson(object expected, object actual)
{
    var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
    var expectedJson = serializer.Serialize(expected);
    var actualJson = serializer.Serialize(actual);
    Assert.AreEqual(expectedJson, actualJson);
}

Parece funcionar muy bien. La información de resultados del corredor de prueba mostrará la comparación de cadenas JSON (el gráfico de objetos) incluida para que pueda ver directamente lo que está mal.

También tenga en cuenta! Si tiene objetos complejos más grandes y solo desea comparar partes de ellos, puede ( usar LINQ para datos de secuencia ) crear objetos anónimos para usar con el método anterior.

public void SomeTest()
{
    var expect = new { PropA = 12, PropB = 14 };
    var sut = loc.Resolve<SomeSvc>();
    var bigObjectResult = sut.Execute(); // This will return a big object with loads of properties 
    AssExt.AreEqualByJson(expect, new { bigObjectResult.PropA, bigObjectResult.PropB });
}
Max
fuente
1
Esta es una excelente manera de probar, especialmente si de todos modos está tratando con JSON (por ejemplo, usando un cliente escrito para acceder a un servicio web). Esta respuesta debería ser mucho más alta.
Roopesh Shenoy
1
Use Linq! @DmitryBLR (véase el último párrafo respuesta) :)
Max
3
Esta es una gran idea. Usaría el Json.NET más nuevo: var esperada Json = Newtonsoft.Json.JsonConvert.SerializeObject (esperado);
BrokeMyLegBiking
2
Esto no funcionará con referencias circulares. Utilice github.com/kbilsted/StatePrinter en su lugar para una experiencia mejorada sobre el enfoque JSON
Carlo V. Dango
2
Eso es cierto @KokaChernov y, a veces, desea fallar la prueba si el orden no es el mismo, pero si no desea fallar si el orden no es el mismo, puede hacer una ordenación explícita (usando linq) en las listas antes de pasarlas al método AreEqualByJson. Una variante simple de "reorganizar" sus objetos antes de la prueba está en el último ejemplo de código en la respuesta. ¡Eso es muy "universal", creo! :)
Max
91

Pruebe la biblioteca FluentAssertions:

dto.ShouldHave(). AllProperties().EqualTo(customer);

http://www.fluentassertions.com/

También se puede instalar usando NuGet.

dkl
fuente
18
ShouldHave ha quedado en desuso, por lo que debe ser dto.ShouldBeEquivalentTo (cliente); en su lugar
WhiteKnight
2
Esta es la mejor respuesta por este motivo .
Todd Menier
ShouldBeEquivalent tiene errores :(
Konstantin Chernov
3
solo tuve el mismo problema y usé lo siguiente, que parece estar funcionando bien:actual.ShouldBeEquivalentTo(expected, x => x.ExcludingMissingMembers())
stt106
1
Esta es una gran lib! No requiere anular Equals y también (si equals se anula de todos modos, por ejemplo, para objetos de valor) no se basa en una implementación correcta. También la diferencia se imprime muy bien, como lo hace Hamcrest para Java.
kap
35

Prefiero no anular Equals solo para habilitar las pruebas. No olvide que si anula Equals, debería anular también GetHashCode o puede obtener resultados inesperados si utiliza sus objetos en un diccionario, por ejemplo.

Me gusta el enfoque de reflexión anterior, ya que atiende la adición de propiedades en el futuro.

Para una solución rápida y simple, sin embargo, a menudo es más fácil crear un método auxiliar que pruebe si los objetos son iguales o implementar IEqualityComparer en una clase que mantenga privada para sus pruebas. Cuando utilice la solución IEqualityComparer, no necesita preocuparse por la implementación de GetHashCode. Por ejemplo:

// Sample class.  This would be in your main assembly.
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// Unit tests
[TestFixture]
public class PersonTests
{
    private class PersonComparer : IEqualityComparer<Person>
    {
        public bool Equals(Person x, Person y)
        {
            if (x == null && y == null)
            {
                return true;
            }

            if (x == null || y == null)
            {
                return false;
            }

            return (x.Name == y.Name) && (x.Age == y.Age);
        }

        public int GetHashCode(Person obj)
        {
            throw new NotImplementedException();
        }
    }

    [Test]
    public void Test_PersonComparer()
    {
        Person p1 = new Person { Name = "Tom", Age = 20 }; // Control data

        Person p2 = new Person { Name = "Tom", Age = 20 }; // Same as control
        Person p3 = new Person { Name = "Tom", Age = 30 }; // Different age
        Person p4 = new Person { Name = "Bob", Age = 20 }; // Different name.

        Assert.IsTrue(new PersonComparer().Equals(p1, p2), "People have same values");
        Assert.IsFalse(new PersonComparer().Equals(p1, p3), "People have different ages.");
        Assert.IsFalse(new PersonComparer().Equals(p1, p4), "People have different names.");
    }
}
Chris Yoxall
fuente
El igual no maneja valores nulos. Agregaría lo siguiente antes de su declaración de devolución en el método igual. if (x == null && y == null) {return true; } if (x == null || y == null) {return false; } Edité la pregunta para agregar soporte nulo.
Bobby Cannon
No funciona para mí con throw new NotImplementedException (); en el GetHashCode. ¿Por qué necesito esa función en IEqualityComparer de cualquier manera?
love2code
15

He intentado varios enfoques mencionados aquí. La mayoría implica serializar sus objetos y hacer una comparación de cadenas. Si bien es muy fácil y, en general, muy efectivo, descubrí que es un poco corto cuando tienes un error y se informa algo como esto:

Expected string length 2326 but was 2342. Strings differ at index 1729.

Averiguar dónde están las diferencias es un dolor, por decir lo menos.

Con las comparaciones de gráficos de objetos de FluentAssertions (es decir a.ShouldBeEquivalentTo(b)), obtienes esto:

Expected property Name to be "Foo" but found "Bar"

Eso es mucho mejor. Obtenga FluentAssertions ahora, se alegrará más tarde (y si votó a favor de esto, también votó a favor la respuesta de dkl donde se sugirió por primera vez FluentAssertions).

Todd Menier
fuente
9

Estoy de acuerdo con ChrisYoxall: implementar Equals en su código principal solo para fines de prueba no es bueno.

Si está implementando Equals porque alguna lógica de la aplicación lo requiere, entonces está bien, pero mantenga el código puro de solo pruebas fuera del desorden (también la semántica de verificar lo mismo para las pruebas puede ser diferente de lo que requiere su aplicación).

En resumen, mantenga el código de solo prueba fuera de su clase.

La comparación simple y superficial de las propiedades mediante la reflexión debería ser suficiente para la mayoría de las clases, aunque es posible que deba repetir si sus objetos tienen propiedades complejas. Si sigue las referencias, tenga cuidado con las referencias circulares o similares.

Astuto

Grifo astuto
fuente
Buena captura de referencias circulares. Fácil de superar si mantiene un diccionario de objetos que ya están en el árbol de comparación.
Lucas B
6

Las restricciones de propiedad , agregadas en NUnit 2.4.2, permiten una solución que es más legible que la original del OP, y produce mensajes de falla mucho mejores. No es de ninguna manera genérico, pero si no necesita hacerlo durante demasiadas clases, es una solución muy adecuada.

Assert.That(ActualObject, Has.Property("Prop1").EqualTo(ExpectedObject.Prop1)
                          & Has.Property("Prop2").EqualTo(ExpectedObject.Prop2)
                          & Has.Property("Prop3").EqualTo(ExpectedObject.Prop3)
                          // ...

No es tan general como la implementación, Equalspero da un mensaje de falla mucho mejor que

Assert.AreEqual(ExpectedObject, ActualObject);
Paul Hicks
fuente
4

La solución JSON de Max Wikstrom (arriba) tiene más sentido para mí, es corta, limpia y, lo más importante, funciona. Personalmente, aunque preferiría implementar la conversión JSON como un método separado y colocar la afirmación nuevamente dentro de la prueba de la unidad de esta manera ...

MÉTODO DE AYUDA:

public string GetObjectAsJson(object obj)
    {
        System.Web.Script.Serialization.JavaScriptSerializer oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
        return oSerializer.Serialize(obj);
    }

PRUEBA DE UNIDAD :

public void GetDimensionsFromImageTest()
        {
            Image Image = new Bitmap(10, 10);
            ImageHelpers_Accessor.ImageDimensions expected = new ImageHelpers_Accessor.ImageDimensions(10,10);

            ImageHelpers_Accessor.ImageDimensions actual;
            actual = ImageHelpers_Accessor.GetDimensionsFromImage(Image);

            /*USING IT HERE >>>*/
            Assert.AreEqual(GetObjectAsJson(expected), GetObjectAsJson(actual));
        }

FYI: es posible que deba agregar una referencia a System.Web.Extensions en su solución.

samaspin
fuente
4

Este es un hilo bastante antiguo, pero me preguntaba si hay una razón por la que no se propone una respuesta NUnit.Framework.Is.EqualToy NUnit.Framework.Is.NotEqualTo.

Como:

Assert.That(LeftObject, Is.EqualTo(RightObject)); 

y

Assert.That(LeftObject, Is.Not.EqualTo(RightObject)); 
usuario2315856
fuente
44
Porque no imprime los detalles de lo que es diferente
Shrage Smilowitz
1

Otra opción es escribir una restricción personalizada implementando la Constraintclase abstracta NUnit . Con una clase auxiliar para proporcionar un poco de azúcar sintáctica, el código de prueba resultante es agradablemente breve y legible, por ejemplo

Assert.That( LeftObject, PortfolioState.Matches( RightObject ) ); 

Para un ejemplo extremo, considere la clase que tiene miembros de 'solo lectura', no es IEquatable, y no podría cambiar la clase bajo prueba incluso si quisiera:

public class Portfolio // Somewhat daft class for pedagogic purposes...
{
    // Cannot be instanitated externally, instead has two 'factory' methods
    private Portfolio(){ }

    // Immutable properties
    public string Property1 { get; private set; }
    public string Property2 { get; private set; }  // Cannot be accessed externally
    public string Property3 { get; private set; }  // Cannot be accessed externally

    // 'Factory' method 1
    public static Portfolio GetPortfolio(string p1, string p2, string p3)
    {
        return new Portfolio() 
        { 
            Property1 = p1, 
            Property2 = p2, 
            Property3 = p3 
        };
    }

    // 'Factory' method 2
    public static Portfolio GetDefault()
    {
        return new Portfolio() 
        { 
            Property1 = "{{NONE}}", 
            Property2 = "{{NONE}}", 
            Property3 = "{{NONE}}" 
        };
    }
}

El contrato para la Constraintclase requiere que uno anule Matchesy WriteDescriptionTo(en el caso de un desajuste, una narrativa para el valor esperado) pero también anular WriteActualValueTo(narrativa para el valor real) tiene sentido:

public class PortfolioEqualityConstraint : Constraint
{
    Portfolio expected;
    string expectedMessage = "";
    string actualMessage = "";

    public PortfolioEqualityConstraint(Portfolio expected)
    {
        this.expected = expected;
    }

    public override bool Matches(object actual)
    {
        if ( actual == null && expected == null ) return true;
        if ( !(actual is Portfolio) )
        { 
            expectedMessage = "<Portfolio>";
            actualMessage = "null";
            return false;
        }
        return Matches((Portfolio)actual);
    }

    private bool Matches(Portfolio actual)
    {
        if ( expected == null && actual != null )
        {
            expectedMessage = "null";
            expectedMessage = "non-null";
            return false;
        }
        if ( ReferenceEquals(expected, actual) ) return true;

        if ( !( expected.Property1.Equals(actual.Property1)
                 && expected.Property2.Equals(actual.Property2) 
                 && expected.Property3.Equals(actual.Property3) ) )
        {
            expectedMessage = expected.ToStringForTest();
            actualMessage = actual.ToStringForTest();
            return false;
        }
        return true;
    }

    public override void WriteDescriptionTo(MessageWriter writer)
    {
        writer.WriteExpectedValue(expectedMessage);
    }
    public override void WriteActualValueTo(MessageWriter writer)
    {
        writer.WriteExpectedValue(actualMessage);
    }
}

Además de la clase auxiliar:

public static class PortfolioState
{
    public static PortfolioEqualityConstraint Matches(Portfolio expected)
    {
        return new PortfolioEqualityConstraint(expected);
    }

    public static string ToStringForTest(this Portfolio source)
    {
        return String.Format("Property1 = {0}, Property2 = {1}, Property3 = {2}.", 
            source.Property1, source.Property2, source.Property3 );
    }
}

Ejemplo de uso:

[TestFixture]
class PortfolioTests
{
    [Test]
    public void TestPortfolioEquality()
    {
        Portfolio LeftObject 
            = Portfolio.GetDefault();
        Portfolio RightObject 
            = Portfolio.GetPortfolio("{{GNOME}}", "{{NONE}}", "{{NONE}}");

        Assert.That( LeftObject, PortfolioState.Matches( RightObject ) );
    }
}
un día cuando
fuente
1

Me basaría en la respuesta de @Juanma. Sin embargo, creo que esto no debería implementarse con afirmaciones de prueba de unidad. Esta es una utilidad que bien podría ser utilizada en algunas circunstancias por código que no sea de prueba.

Escribí un artículo sobre el tema http://timoch.com/blog/2013/06/unit-test-equality-is-not-domain-equality/

Mi propuesta es la siguiente:

/// <summary>
/// Returns the names of the properties that are not equal on a and b.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns>An array of names of properties with distinct 
///          values or null if a and b are null or not of the same type
/// </returns>
public static string[] GetDistinctProperties(object a, object b) {
    if (object.ReferenceEquals(a, b))
        return null;
    if (a == null)
        return null;
    if (b == null)
        return null;

    var aType = a.GetType();
    var bType = b.GetType();

    if (aType != bType)
        return null;

    var props = aType.GetProperties();

    if (props.Any(prop => prop.GetIndexParameters().Length != 0))
        throw new ArgumentException("Types with index properties not supported");

    return props
        .Where(prop => !Equals(prop.GetValue(a, null), prop.GetValue(b, null)))
        .Select(prop => prop.Name).ToArray();
} 

Usando esto con NUnit

Expect(ReflectionUtils.GetDistinctProperties(tile, got), Empty);

produce el siguiente mensaje en caso de falta de coincidencia.

Expected: <empty>
But was:  < "MagmaLevel" >
at NUnit.Framework.Assert.That(Object actual, IResolveConstraint expression, String message, Object[] args)
at Undermine.Engine.Tests.TileMaps.BasicTileMapTests.BasicOperations() in BasicTileMapTests.cs: line 29
TiMoch
fuente
1

https://github.com/kbilsted/StatePrinter se ha escrito específicamente para volcar gráficos de objetos en la representación de cadenas con el objetivo de escribir pruebas unitarias fáciles.

  • Viene con métodos Assert que generan una cadena correctamente escapada, fácil de copiar y pegar en la prueba para corregirla.
  • Permite que unittest se reescriba automáticamente
  • Se integra con todos los marcos de pruebas unitarias.
  • A diferencia de la serialización JSON, se admiten referencias circulares
  • Puede filtrar fácilmente, por lo que solo se descargan partes de tipos

Dado

class A
{
  public DateTime X;
  public DateTime Y { get; set; }
  public string Name;
}

Puede escribir de forma segura y utilizando la finalización automática de Visual Studio incluir o excluir campos.

  var printer = new Stateprinter();
  printer.Configuration.Projectionharvester().Exclude<A>(x => x.X, x => x.Y);

  var sut = new A { X = DateTime.Now, Name = "Charly" };

  var expected = @"new A(){ Name = ""Charly""}";
  printer.Assert.PrintIsSame(expected, sut);
Carlo V. Dango
fuente
1

Simplemente instale ExpectedObjects de Nuget, puede comparar fácilmente el valor de propiedad de dos objetos, el valor de cada objeto de colección, el valor de dos objetos compuestos y el valor de propiedad de comparación parcial por tipo anónimo.

Tengo algunos ejemplos en github: https://github.com/hatelove/CompareObjectEquals

Aquí hay algunos ejemplos que contienen escenarios de comparación de objetos:

    [TestMethod]
    public void Test_Person_Equals_with_ExpectedObjects()
    {
        //use extension method ToExpectedObject() from using ExpectedObjects namespace to project Person to ExpectedObject
        var expected = new Person
        {
            Id = 1,
            Name = "A",
            Age = 10,
        }.ToExpectedObject();

        var actual = new Person
        {
            Id = 1,
            Name = "A",
            Age = 10,
        };

        //use ShouldEqual to compare expected and actual instance, if they are not equal, it will throw a System.Exception and its message includes what properties were not match our expectation.
        expected.ShouldEqual(actual);
    }

    [TestMethod]
    public void Test_PersonCollection_Equals_with_ExpectedObjects()
    {
        //collection just invoke extension method: ToExpectedObject() to project Collection<Person> to ExpectedObject too
        var expected = new List<Person>
        {
            new Person { Id=1, Name="A",Age=10},
            new Person { Id=2, Name="B",Age=20},
            new Person { Id=3, Name="C",Age=30},
        }.ToExpectedObject();

        var actual = new List<Person>
        {
            new Person { Id=1, Name="A",Age=10},
            new Person { Id=2, Name="B",Age=20},
            new Person { Id=3, Name="C",Age=30},
        };

        expected.ShouldEqual(actual);
    }

    [TestMethod]
    public void Test_ComposedPerson_Equals_with_ExpectedObjects()
    {
        //ExpectedObject will compare each value of property recursively, so composed type also simply compare equals.
        var expected = new Person
        {
            Id = 1,
            Name = "A",
            Age = 10,
            Order = new Order { Id = 91, Price = 910 },
        }.ToExpectedObject();

        var actual = new Person
        {
            Id = 1,
            Name = "A",
            Age = 10,
            Order = new Order { Id = 91, Price = 910 },
        };

        expected.ShouldEqual(actual);
    }

    [TestMethod]
    public void Test_PartialCompare_Person_Equals_with_ExpectedObjects()
    {
        //when partial comparing, you need to use anonymous type too. Because only anonymous type can dynamic define only a few properties should be assign.
        var expected = new
        {
            Id = 1,
            Age = 10,
            Order = new { Id = 91 }, // composed type should be used anonymous type too, only compare properties. If you trace ExpectedObjects's source code, you will find it invoke config.IgnoreType() first.
        }.ToExpectedObject();

        var actual = new Person
        {
            Id = 1,
            Name = "B",
            Age = 10,
            Order = new Order { Id = 91, Price = 910 },
        };

        // partial comparing use ShouldMatch(), rather than ShouldEqual()
        expected.ShouldMatch(actual);
    }

Referencia:

  1. Objetos esperados github
  2. Introducción de objetos esperados
En91
fuente
1

Terminé escribiendo una fábrica de expresiones simples:

public static class AllFieldsEqualityComprision<T>
{
    public static Comparison<T> Instance { get; } = GetInstance();

    private static Comparison<T> GetInstance()
    {
        var type = typeof(T);
        ParameterExpression[] parameters =
        {
            Expression.Parameter(type, "x"),
            Expression.Parameter(type, "y")
        };
        var result = type.GetProperties().Aggregate<PropertyInfo, Expression>(
            Expression.Constant(true),
            (acc, prop) =>
                Expression.And(acc,
                    Expression.Equal(
                        Expression.Property(parameters[0], prop.Name),
                        Expression.Property(parameters[1], prop.Name))));
        var areEqualExpression = Expression.Condition(result, Expression.Constant(0), Expression.Constant(1));
        return Expression.Lambda<Comparison<T>>(areEqualExpression, parameters).Compile();
    }
}

y solo úsalo:

Assert.That(
    expectedCollection, 
    Is.EqualTo(actualCollection)
      .Using(AllFieldsEqualityComprision<BusinessCategoryResponse>.Instance));

Es muy útil ya que tengo que comparar la colección de tales objetos. Y puedes usar esta comparación en otro lugar :)

Aquí está lo esencial con un ejemplo: https://gist.github.com/Pzixel/b63fea074864892f9aba8ffde312094f

Alex Zhukovskiy
fuente
0

Deserialice ambas clases y haga una comparación de cadenas.

EDITAR: funciona perfectamente, esta es la salida que obtengo de NUnit;

Test 'Telecom.SDP.SBO.App.Customer.Translator.UnitTests.TranslateEaiCustomerToDomain_Tests.TranslateNew_GivenEaiCustomer_ShouldTranslateToDomainCustomer_Test("ApprovedRatingInDb")' failed:
  Expected string length 2841 but was 5034. Strings differ at index 443.
  Expected: "...taClasses" />\r\n  <ContactMedia />\r\n  <Party i:nil="true" /..."
  But was:  "...taClasses" />\r\n  <ContactMedia>\r\n    <ContactMedium z:Id="..."
  ----------------------------------------------^
 TranslateEaiCustomerToDomain_Tests.cs(201,0): at Telecom.SDP.SBO.App.Customer.Translator.UnitTests.TranslateEaiCustomerToDomain_Tests.Assert_CustomersAreEqual(Customer expectedCustomer, Customer actualCustomer)
 TranslateEaiCustomerToDomain_Tests.cs(114,0): at Telecom.SDP.SBO.App.Customer.Translator.UnitTests.TranslateEaiCustomerToDomain_Tests.TranslateNew_GivenEaiCustomer_ShouldTranslateToDomainCustomer_Test(String custRatingScenario)

EDITAR DOS: Los dos objetos pueden ser idénticos, pero el orden en el que se serializan las propiedades no es el mismo. Por lo tanto, el XML es diferente. DOH!

EDITAR TRES: Esto funciona. Lo estoy usando en mis pruebas. Pero debe agregar elementos a las propiedades de la colección en el orden en que el código bajo prueba los agrega.

Casey Burns
fuente
1
serializar ? Idea interesante. Sin embargo, no estoy seguro de cómo se mantendría en términos de rendimiento
Michael Haren
no le permitirá comparar dobles o decimales con una precisión dada.
Noctis
0

Sé que esta es una pregunta muy antigua, pero NUnit todavía no tiene soporte nativo para esto. Sin embargo, si le gustan las pruebas de estilo BDD (ala Jasmine), se sorprenderá gratamente con NExpect ( https://github.com/fluffynuts/NExpect , obténgalo de NuGet), que tiene pruebas de igualdad profunda al horno allí mismo .

(descargo de responsabilidad: soy el autor de NExpect)

daf
fuente
-1

Stringify y compara dos cadenas

Assert.AreEqual (JSON.stringify (LeftObject), JSON.stringify (RightObject))

jmtt89
fuente
-1
//Below works precisely well, Use it.
private void CompareJson()
{
object expected = new object();
object actual = new object();
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var expectedResponse = serializer.Serialize(expected);
var actualResponse = serializer.Serialize(actual);
Assert.AreEqual(expectedResponse, actualResponse);
}
Satish Babu
fuente
Gracias por este fragmento de código, que podría proporcionar una ayuda limitada a corto plazo. Una explicación adecuada mejoraría en gran medida su valor a largo plazo al mostrar por qué esta es una buena solución al problema y lo haría más útil para futuros lectores con otras preguntas similares. Por favor, editar su respuesta a añadir un poco de explicación, incluyendo los supuestos realizados.
Toby Speight
¿Y qué agrega esto a la respuesta de Max ?
Toby Speight