Confronta l'uguaglianza tra due oggetti in NUnit


126

Sto cercando di affermare che un oggetto è "uguale" a un altro oggetto.

Gli oggetti sono solo istanze di una classe con un mucchio di proprietà pubbliche. C'è un modo semplice per far sì che NUnit asserisca l'uguaglianza in base alle proprietà?

Questa è la mia soluzione attuale ma penso che potrebbe esserci qualcosa di meglio:

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

Quello che sto cercando sarebbe nello stesso spirito del CollectionEquivalentConstraint in cui NUnit verifica che il contenuto di due raccolte sia identico.

Risposte:


51

Override .Equals per il tuo oggetto e nel test unitario puoi semplicemente fare questo:

Assert.AreEqual(LeftObject, RightObject);

Ovviamente, questo potrebbe significare che devi spostare tutti i singoli confronti sul metodo .Equals, ma ti permetterebbe di riutilizzare l'implementazione per più test, e probabilmente ha senso avere se gli oggetti dovessero essere in grado di confrontarsi con i fratelli comunque.


2
Grazie Lassevk. Questo ha funzionato per me! Ho implementato .Equals secondo le linee guida qui: msdn.microsoft.com/en-us/library/336aedhh(VS.80).aspx
Michael Haren,

12
E GetHashCode (), ovviamente ;-p
Marc Gravell

Il numero 1 nell'elenco in quella pagina è quello di sovrascrivere GetHashCode, e ha detto di aver seguito quelle linee guida :) Ma sì, errore comune ignorarlo. In genere non è un errore che noterai la maggior parte delle volte, ma quando lo fai, è come una di quelle volte in cui dici "Oh, ehi, perché questo serpente mi fa i pantaloni e perché mi morde il culo".
Lasse V. Karlsen,

1
Un avvertimento importante: se anche il tuo oggetto viene implementato IEnumerable, verrà confrontato come una raccolta indipendentemente dalle implementazioni Equalsprioritarie perché NUnit dà la IEnumerableprecedenza. Vedi i NUnitEqualityComparer.AreEqualmetodi per i dettagli. È possibile ignorare il comparatore utilizzando uno dei Using()metodi del vincolo di uguaglianza . Anche in questo caso, non è sufficiente implementare il non generico a IEqualityComparercausa dell'adattatore utilizzato da NUnit.
Kaleb Pederson,

13
Altro avvertimento: l'implementazione GetHashCode()su tipi mutabili si comporterà in modo anomalo se si utilizza quell'oggetto come chiave. IMHO, ignorando Equals(), GetHashCode()e rendendo l'immutabile oggetto solo per testare non ha senso.
bavaza,

118

Se non è possibile ignorare Equals per qualsiasi motivo, è possibile creare un metodo di supporto che scorre attraverso le proprietà pubbliche riflettendo e affermando ciascuna proprietà. Qualcosa come questo:

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]);
    }
}

@wesley: questo non è vero. Metodo Type.GetProperties: restituisce tutte le proprietà pubbliche del tipo corrente. Vedi msdn.microsoft.com/en-us/library/aky14axb.aspx
Sergii Volchkov

4
Grazie. tuttavia, ho dovuto cambiare l'ordine dei parametri effettivi e previsti poiché la previsione è che l'atteso è un parametro prima dell'attuale.
Valamas,

questo è un approccio migliore IMHO, le sostituzioni Equal & HashCode non dovrebbero essere basate sul confronto di ogni campo e in più è molto noioso da fare su ogni oggetto. Buon lavoro!
Scott White,

3
Funziona alla grande se il tuo tipo ha solo tipi di base come proprietà. Tuttavia, se il tuo tipo ha proprietà con tipi personalizzati (che non implementano Equals), fallirà.
Bobby Cannon,

Aggiunta della ricorsione per le proprietà degli oggetti, ma ho dovuto saltare le proprietà indicizzate:
cerhart,

113

Non ignorare Equals solo a scopo di test. È noioso e influisce sulla logica del dominio. Anziché,

Utilizzare JSON per confrontare i dati dell'oggetto

Nessuna logica aggiuntiva sui tuoi oggetti. Nessuna attività aggiuntiva per i test.

Usa questo semplice metodo:

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

Sembra funzionare alla grande. Le informazioni sui risultati del runner di test mostreranno il confronto delle stringhe JSON (il grafico degli oggetti) incluso in modo da vedere direttamente cosa non va.

Nota anche! Se hai oggetti complessi più grandi e vuoi solo confrontare parti di essi, puoi ( usa LINQ per i dati di sequenza ) creare oggetti anonimi da usare con il metodo sopra.

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

1
Questo è un ottimo modo per testare, specialmente se hai comunque a che fare con JSON (ad es. Usando un client tipizzato per accedere a un servizio web). Questa risposta dovrebbe essere molto più alta.
Roopesh Shenoy,

1
Usa Linq! @DmitryBLR (vedi l'ultimo paragrafo in risposta) :)
Max

3
Questa è una grande idea. Vorrei utilizzare il più recente Json.NET: var expectedJson = Newtonsoft.Json.JsonConvert.SerializeObject (previsto);
BrokeMyLegBiking

2
Questo non funzionerà con riferimenti circolari. Usa github.com/kbilsted/StatePrinter invece per una migliore esperienza sull'approccio JSON
Carlo V. Dango

2
È vero @KokaChernov e a volte vuoi fallire il test se l'ordine non è lo stesso ma se non vuoi fallire se l'ordinamento non è lo stesso puoi fare un ordinamento esplicito (usando linq) sugli elenchi prima di passarli al metodo AreEqualByJson. Una semplice variante di "riordinare" i tuoi oggetti prima del test è nell'ultimo esempio di codice nella risposta. Quindi è molto "universale" penso! :)
Max

91

Prova la libreria FluentAssertions:

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

http://www.fluentassertions.com/

Può anche essere installato usando NuGet.


18
ShouldHave è stato deprecato, quindi dovrebbe essere dto.ShouldBeEquivalentTo (cliente); invece
WhiteKnight il

2
Questa è la migliore risposta per questo motivo .
Todd Menier,

ShouldBeEquivalent è buggy :(
Konstantin Chernov,

3
ha appena avuto lo stesso problema e ha usato quanto segue che sembra funzionare bene:actual.ShouldBeEquivalentTo(expected, x => x.ExcludingMissingMembers())
stt106

1
Questa è una grande lib! Non richiede l'override di Equals e inoltre (se uguali viene comunque sostituito, ad es. Per oggetti valore) non si basa sulla corretta implementazione. Inoltre, la differenza è ben stampata, come Hamcrest per Java.
Kap

35

Preferisco non ignorare Equals solo per abilitare i test. Non dimenticare che se esegui l'override di Equals dovresti davvero ignorare GetHashCode o potresti ottenere risultati imprevisti se, ad esempio, stai utilizzando i tuoi oggetti in un dizionario.

Mi piace l'approccio di riflessione sopra dato che si rivolge per l'aggiunta di proprietà in futuro.

Per una soluzione rapida e semplice, tuttavia, è spesso più semplice creare un metodo di supporto che verifica se gli oggetti sono uguali o implementare IEqualityComparer su una classe che si mantiene privata per i test. Quando si utilizza la soluzione IEqualityComparer non è necessario preoccuparsi dell'implementazione di GetHashCode. Per esempio:

// 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.");
    }
}

Il uguale non gestisce i valori null. Aggiungerei quanto segue prima della tua dichiarazione di ritorno nel metodo equals. if (x == null && y == null) {return true; } if (x == null || y == null) {return false; } Ho modificato la domanda per aggiungere un supporto null.
Bobby Cannon,

Non funziona per me con il lancio di NotImplementedException (); nel GetHashCode. Perché ho bisogno di quella funzione in IEqualityComparer in entrambi i modi?
love2code

15

Ho provato diversi approcci citati qui. La maggior parte comporta la serializzazione dei tuoi oggetti e il confronto delle stringhe. Mentre super facile e generalmente molto efficace, ho scoperto che si presenta un po 'corto quando si verifica un errore e viene segnalato qualcosa del genere:

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

Capire dove sono le differenze è a dir poco doloroso.

Con i confronti del grafico a oggetti di FluentAssertions (ovvero a.ShouldBeEquivalentTo(b)), si ottiene questo:

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

È molto più bello. Ottieni subito FluentAssertions , sarai contento più tardi (e se lo voti, per favore vota anche la risposta di dkl dove FluentAssertions è stato suggerito per la prima volta).


9

Sono d'accordo con ChrisYoxall - l'implementazione di Equals nel tuo codice principale a puro scopo di test non è buona.

Se stai implementando Equals perché alcune logiche applicative lo richiedono, va bene, ma tieni il codice puro solo per i test fuori dagli ingombri (anche la semantica del controllo degli stessi per i test potrebbe essere diversa da quella richiesta dalla tua app).

In breve, mantieni il codice di solo test fuori dalla tua classe.

Un semplice confronto superficiale delle proprietà usando la riflessione dovrebbe essere sufficiente per la maggior parte delle classi, anche se potrebbe essere necessario ricorrere se gli oggetti hanno proprietà complesse. Se si seguono i riferimenti, fare attenzione ai riferimenti circolari o simili.

sornione


Bella cattura di riferimenti circolari. Facile da superare se si mantiene un dizionario di oggetti già nella struttura di confronto.
Lucas B,

6

I vincoli di proprietà , aggiunti in NUnit 2.4.2, consentono una soluzione più leggibile di quella originale dell'OP e produce messaggi di errore molto migliori. Non è in alcun modo generico, ma se non è necessario farlo per troppe classi, è una soluzione molto adeguata.

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

Non generico come l'implementazione, Equalsma fornisce un messaggio di errore molto migliore di

Assert.AreEqual(ExpectedObject, ActualObject);

4

La soluzione JSON di Max Wikstrom (sopra) per me ha più senso, è breve, pulita e, soprattutto, funziona. Personalmente, preferirei implementare la conversione JSON come metodo separato e rimettere l'asserzione all'interno del test unitario in questo modo ...

METODO DI AIUTO:

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

TEST DELL'UNITÀ:

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

Cordiali saluti - Potrebbe essere necessario aggiungere un riferimento a System.Web.Extensions nella soluzione.


4

Questo è un thread piuttosto vecchio, ma mi chiedevo se c'è un motivo per cui nessuna risposta è stata proposta NUnit.Framework.Is.EqualToe NUnit.Framework.Is.NotEqualTo?

Ad esempio:

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

e

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

4
Perché non stampa i dettagli di ciò che è diverso
Shrage Smilowitz,

1

Un'altra opzione è scrivere un vincolo personalizzato implementando la Constraintclasse astratta NUnit . Con una classe di supporto per fornire un po 'di zucchero sintattico, il codice del test risultante è piacevolmente conciso e leggibile, ad es

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

Per un esempio estremo, considera la classe che ha membri 'di sola lettura', non lo è e non IEquatablepuoi cambiare la classe sotto test anche se volessi:

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

Il contratto per la Constraintclasse richiede che uno abbia la precedenza Matchese WriteDescriptionTo(nel caso di una mancata corrispondenza, una narrazione per il valore atteso) ma anche la sostituzione WriteActualValueTo(narrativa per il valore reale) ha senso:

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

Inoltre la classe helper:

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

Esempio di utilizzo:

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

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

1

Vorrei basarmi sulla risposta di @Juanma. Tuttavia, credo che questo non dovrebbe essere implementato con asserzioni di unit test. Questa è un'utilità che potrebbe essere utilizzata in alcune circostanze da un codice non di prova.

Ho scritto un articolo sull'argomento http://timoch.com/blog/2013/06/unit-test-equality-is-not-domain-equality/

La mia proposta è la seguente:

/// <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();
} 

Usandolo con NUnit

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

produce il seguente messaggio in caso di mancata corrispondenza.

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

1

https://github.com/kbilsted/StatePrinter è stato scritto appositamente per scaricare i grafici degli oggetti nella rappresentazione di stringhe allo scopo di scrivere semplici test unitari.

  • Viene fornito con i metodi Assert che generano una stringa con escape corretta, facile da copiare e incollare nel test per correggerlo.
  • Consente di riscrivere automaticamente unittest
  • Si integra con tutti i framework di test unitari
  • A differenza della serializzazione JSON, sono supportati riferimenti circolari
  • Puoi facilmente filtrare, quindi vengono scaricate solo parti di tipi

Dato

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

È possibile digitare in modo sicuro e, utilizzando il completamento automatico di Visual Studio, includere o escludere campi.

  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);

1

Basta installare ExpectedObjects da Nuget, è possibile confrontare facilmente il valore della proprietà di due oggetti, il valore di ciascun oggetto della raccolta, il valore di due oggetti composti e il valore della proprietà di confronto parziale per tipo anonimo.

Ho alcuni esempi su github: https://github.com/hatelove/CompareObjectEquals

Ecco alcuni esempi che contengono scenari di confronto di oggetti:

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

Riferimento:

  1. ExpectedObjects github
  2. Introduzione di ExpectedObjects


1

Ho finito con la scrittura di una semplice fabbrica di espressioni:

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();
    }
}

e basta usarlo:

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

È molto utile poiché devo confrontare la raccolta di tali oggetti. E puoi usare questo confronto da qualche altra parte :)

Ecco un esempio con l'esempio: https://gist.github.com/Pzixel/b63fea074864892f9aba8ffde312094f


0

Deserializza entrambe le classi ed esegui un confronto di stringhe.

EDIT: funziona perfettamente, questo è l'output che ottengo da 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)

MODIFICA DUE: I due oggetti possono essere identici, ma l'ordine di serializzazione delle proprietà non è lo stesso. Pertanto l'XML è diverso. DOH!

EDIT TRE: Questo funziona. Lo sto usando nei miei test. Ma è necessario aggiungere elementi alle proprietà della raccolta nell'ordine in cui vengono aggiunti dal codice in prova.


1
serializzare ? Idea interessante. Non sono sicuro di come reggerebbe in termini di prestazioni
Michael Haren,

non ti permetterà di confrontare doppi o decimali con una data precisione.
Noctis,

0

So che questa è una domanda molto vecchia, ma NUnit non ha ancora il supporto nativo per questo. Tuttavia, se ti piacciono i test in stile BDD (ala Jasmine), rimarrai piacevolmente sorpreso da NExpect ( https://github.com/fluffynuts/NExpect , ottenerlo da NuGet), che ha test di uguaglianza profondi proprio lì dentro .

(dichiarazione di non responsabilità: sono l'autore di NExpect)


-1

Stringi e confronta due stringhe

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


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

Grazie per questo frammento di codice, che potrebbe fornire un aiuto limitato a breve termine. Una spiegazione adeguata migliorerebbe notevolmente il suo valore a lungo termine mostrando perché questa è una buona soluzione al problema e la renderebbe più utile ai futuri lettori con altre domande simili. Si prega di modificare la risposta di aggiungere qualche spiegazione, tra le ipotesi che hai fatto.
Toby Speight,

E cosa aggiunge questo alla risposta di Max ?
Toby Speight,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.