Comparer l'égalité entre deux objets dans NUnit

126

J'essaye d'affirmer qu'un objet est "égal" à un autre objet.

Les objets ne sont que des instances d'une classe avec un tas de propriétés publiques. Existe-t-il un moyen simple de faire affirmer l'égalité par NUnit en fonction des propriétés?

C'est ma solution actuelle mais je pense qu'il y a peut-être quelque chose de mieux:

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

Ce que je veux serait dans le même esprit que le CollectionEquivalentConstraint dans lequel NUnit vérifie que le contenu de deux collections est identique.

Michael Haren
la source

Réponses:

51

Remplacez .Equals pour votre objet et dans le test unitaire, vous pouvez alors simplement faire ceci:

Assert.AreEqual(LeftObject, RightObject);

Bien sûr, cela peut signifier que vous déplacez simplement toutes les comparaisons individuelles vers la méthode .Equals, mais cela vous permettrait de réutiliser cette implémentation pour plusieurs tests, et il est probablement logique de savoir si les objets doivent pouvoir se comparer avec leurs frères et sœurs de toute façon.

Lasse V. Karlsen
la source
2
Merci, lassevk. Cela a fonctionné pour moi! J'ai implémenté .Equals selon les directives ici: msdn.microsoft.com/en-us/library/336aedhh(VS.80).aspx
Michael Haren
12
Et GetHashCode (), évidemment ;-p
Marc Gravell
Le numéro 1 sur la liste sur cette page est de remplacer GetHashCode, et il a dit qu'il suivait ces directives :) Mais oui, erreur courante d'ignorer cela. Ce n'est généralement pas une erreur que vous remarquerez la plupart du temps, mais quand vous le faites, c'est comme une de ces fois où vous dites "Oh, hé, pourquoi ce serpent dans mon pantalon et pourquoi me mord-il le cul".
Lasse V. Karlsen
1
Une mise en garde importante: si votre objet implémente également, IEnumerableil sera comparé en tant que collection indépendamment des implémentations de Equalssubstitution, car NUnit donne IEnumerableune priorité plus élevée. Voir les NUnitEqualityComparer.AreEqualméthodes pour plus de détails. Vous pouvez remplacer le comparateur à l'aide de l'une des Using()méthodes de la contrainte d'égalité . Même dans ce cas, il n'est pas suffisant d'implémenter le non générique en IEqualityComparerraison de l'adaptateur utilisé par NUnit.
Kaleb Pederson
13
Plus de mise en garde: la mise GetHashCode()en œuvre sur des types mutables se comportera mal si jamais vous utilisez cet objet comme clé. À mon humble avis, remplaçant Equals(), GetHashCode()et de faire l'objet immuable de juste pour tester n'a pas de sens.
bavaza
118

Si vous ne pouvez pas remplacer Equals pour une raison quelconque, vous pouvez créer une méthode d'assistance qui itère à travers les propriétés publiques par réflexion et affirmer chaque propriété. Quelque chose comme ça:

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
la source
@wesley: ce n'est pas vrai. Type.GetProperties, méthode: renvoie toutes les propriétés publiques du type actuel. Voir msdn.microsoft.com/en-us/library/aky14axb.aspx
Sergii Volchkov
4
Merci. cependant, j'ai dû changer l'ordre des paramètres réels et attendus car la conversion est celle attendue est un paramètre avant réel.
Valamas
c'est une meilleure approche IMHO, les remplacements Equal & HashCode ne devraient pas avoir à être basés sur la comparaison de chaque champ et en plus c'est très fastidieux à faire pour chaque objet. Bon travail!
Scott White
3
Cela fonctionne très bien si votre type n'a que des types de base comme propriétés. Cependant, si votre type a des propriétés avec des types personnalisés (qui n'implémentent pas Equals), il échouera.
Bobby Cannon
Ajout d'une récursion pour les propriétés des objets, mais j'ai dû ignorer les propriétés indexées:
cerhart
113

Ne remplacez pas Equals uniquement à des fins de test. C'est fastidieux et affecte la logique du domaine. Au lieu,

Utilisez JSON pour comparer les données de l'objet

Aucune logique supplémentaire sur vos objets. Aucune tâche supplémentaire pour les tests.

Utilisez simplement cette méthode 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);
}

Cela semble bien fonctionner. Les informations sur les résultats du testeur afficheront la comparaison de chaînes JSON (le graphique d'objet) incluse afin que vous voyiez directement ce qui ne va pas.

Notez également! Si vous avez des objets complexes plus volumineux et que vous souhaitez simplement en comparer des parties, vous pouvez ( utilisez LINQ pour les données de séquence ) créer des objets anonymes à utiliser avec la méthode ci-dessus.

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
la source
1
C'est un excellent moyen de tester, surtout si vous avez de toute façon affaire à JSON (par exemple en utilisant un client typé pour accéder à un service Web). Cette réponse devrait être beaucoup plus élevée.
Roopesh Shenoy
1
Utilisez Linq! @DmitryBLR (voir le dernier paragraphe de la réponse) :)
Max
3
C'est une bonne idée. J'utiliserais le plus récent Json.NET: var attenduJson = Newtonsoft.Json.JsonConvert.SerializeObject (attendu);
BrokeMyLegBiking
2
Cela ne fonctionnera pas avec des références circulaires. Utilisez plutôt github.com/kbilsted/StatePrinter pour une expérience améliorée par rapport à l'approche JSON
Carlo V. Dango
2
C'est vrai @KokaChernov et parfois vous voulez échouer au test sinon l'ordre est le même mais si vous ne voulez pas échouer si l'ordre n'est pas le même, vous pouvez faire un tri explicite (en utilisant linq) sur les listes avant de les passer à la méthode AreEqualByJson. Une variante simple de "réorganiser" vos objets avant les tests se trouve dans le dernier exemple de code de la réponse. C'est donc très "universel" je pense! :)
Max
91

Essayez la bibliothèque FluentAssertions:

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

http://www.fluentassertions.com/

Il peut également être installé à l'aide de NuGet.

dkl
la source
18
ShouldHave est obsolète, il doit donc être dto.ShouldBeEquivalentTo (client); à la place
WhiteKnight
2
C'est la meilleure réponse pour cette raison .
Todd Menier
ShouldBeEquivalent est buggy :(
Konstantin Chernov
3
vient d'avoir le même problème et a utilisé ce qui suit qui semble fonctionner actual.ShouldBeEquivalentTo(expected, x => x.ExcludingMissingMembers())
correctement
1
C'est une super lib! Ne nécessite pas de surcharger Equals et aussi (si égal est de toute façon surchargé, par exemple pour les objets valeur) ne repose pas sur une implémentation correcte. La différence est également bien imprimée, comme Hamcrest le fait pour Java.
kap
35

Je préfère ne pas remplacer Equals juste pour activer les tests. N'oubliez pas que si vous remplacez Equals, vous devez vraiment remplacer GetHashCode ou vous pouvez obtenir des résultats inattendus si vous utilisez vos objets dans un dictionnaire par exemple.

J'aime l'approche de réflexion ci-dessus car elle permet l'ajout de propriétés à l'avenir.

Pour une solution simple et rapide, il est souvent plus simple de créer une méthode d'assistance qui teste si les objets sont égaux ou d'implémenter IEqualityComparer sur une classe que vous gardez privée pour vos tests. Lorsque vous utilisez la solution IEqualityComparer, vous n'avez pas besoin de vous soucier de l'implémentation de GetHashCode. Par exemple:

// 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
la source
L'égalité ne gère pas les valeurs nulles. J'ajouterais ce qui suit avant votre déclaration de retour dans la méthode égale. if (x == null && y == null) {retourne vrai; } if (x == null || y == null) {return false; } J'ai modifié la question pour ajouter une prise en charge nulle.
Bobby Cannon
Ne fonctionne pas pour moi avec throw new NotImplementedException (); dans le GetHashCode. Pourquoi ai-je besoin de cette fonction dans IEqualityComparer de toute façon?
love2code
15

J'ai essayé plusieurs approches mentionnées ici. La plupart impliquent la sérialisation de vos objets et une comparaison de chaînes. Bien que super facile et généralement très efficace, j'ai trouvé que cela arrivait un peu court lorsque vous avez un échec et que quelque chose comme ça est signalé:

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

Déterminer où se trouvent les différences est pour le moins pénible.

Avec les comparaisons de graphes d'objets de FluentAssertions (ie a.ShouldBeEquivalentTo(b)), vous récupérez ceci:

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

C'est beaucoup mieux. Obtenez FluentAssertions maintenant, vous serez heureux plus tard (et si vous votez pour cela, veuillez également voter pour la réponse de dkl où FluentAssertions a été suggéré pour la première fois).

Todd Menier
la source
9

Je suis d'accord avec ChrisYoxall - implémenter Equals dans votre code principal uniquement à des fins de test n'est pas bon.

Si vous implémentez Equals parce que certaines logiques d'application l'exigent, c'est très bien, mais évitez d'encombrer le code de test uniquement (la sémantique de la vérification de la même chose pour les tests peut également être différente de celle requise par votre application).

En bref, gardez le code de test uniquement hors de votre classe.

Une simple comparaison superficielle des propriétés à l'aide de la réflexion devrait suffire pour la plupart des classes, même si vous devrez peut-être répéter si vos objets ont des propriétés complexes. Si vous suivez des références, méfiez-vous des références circulaires ou similaires.

Sournois

Griffon rusé
la source
Belle prise sur les références circulaires. Facile à surmonter si vous conservez déjà un dictionnaire d'objets dans l'arbre de comparaison.
Lucas B
6

Les contraintes de propriété , ajoutées dans NUnit 2.4.2, permettent une solution plus lisible que celle d'origine de l'OP, et elle produit de bien meilleurs messages d'échec. Ce n'est en aucun cas générique, mais si vous n'avez pas besoin de le faire pour trop de classes, c'est une solution très adéquate.

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

Pas aussi général que la mise en œuvre, Equalsmais cela donne un bien meilleur message d'échec que

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

La solution JSON de Max Wikstrom (ci-dessus) est la plus logique pour moi, elle est courte, propre et surtout elle fonctionne. Personnellement, je préfère implémenter la conversion JSON en tant que méthode distincte et replacer l'assertion dans le test unitaire comme ceci ...

MÉTHODE D'AIDE:

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

TEST DE L'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));
        }

FYI - Vous devrez peut-être ajouter une référence à System.Web.Extensions dans votre solution.

samaspin
la source
4

C'est un fil assez ancien, mais je me demandais s'il y avait une raison pour laquelle aucune réponse n'est proposée NUnit.Framework.Is.EqualToet NUnit.Framework.Is.NotEqualTo?

Tel que:

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

et

Assert.That(LeftObject, Is.Not.EqualTo(RightObject)); 
utilisateur2315856
la source
4
Parce qu'il n'imprime pas les détails de ce qui est différent
Shrage Smilowitz
1

Une autre option consiste à écrire une contrainte personnalisée en implémentant la Constraintclasse abstraite NUnit . Avec une classe d'assistance pour fournir un peu de sucre syntaxique, le code de test résultant est agréablement concis et lisible, par exemple

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

Pour un exemple extrême, considérez la classe qui a des membres en lecture seule, n'est pas IEquatable, et vous ne pourriez pas changer la classe testée même si vous vouliez:

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

Le contrat pour la Constraintclasse exige que l'on remplace Matcheset WriteDescriptionTo(dans le cas d'une discordance, un récit pour la valeur attendue), mais aussi le dépassement WriteActualValueTo(récit pour la valeur réelle) a du sens:

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

Plus la classe d'assistance:

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

Exemple d'utilisation:

[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 jour quand
la source
1

Je miserais sur la réponse de @Juanma. Cependant, je pense que cela ne devrait pas être implémenté avec des assertions de test unitaire. C'est un utilitaire qui pourrait très bien être utilisé dans certaines circonstances par du code non test.

J'ai écrit un article sur le sujet http://timoch.com/blog/2013/06/unit-test-equality-is-not-domain-equality/

Ma proposition est la suivante:

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

Utilisation de ceci avec NUnit

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

renvoie le message suivant en cas d'incompatibilité.

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
la source
1

https://github.com/kbilsted/StatePrinter a été écrit spécifiquement pour vider les graphiques d'objets en représentation sous forme de chaîne dans le but d'écrire des tests unitaires faciles.

  • Il est fourni avec des méthodes Assert qui produisent une chaîne correctement échappée, un copier-coller facile dans le test pour le corriger.
  • Il permet à unittest d'être réécrit automatiquement
  • Il s'intègre à tous les frameworks de tests unitaires
  • Contrairement à la sérialisation JSON, les références circulaires sont prises en charge
  • Vous pouvez facilement filtrer, de sorte que seules les parties des types sont vidées

Donné

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

Vous pouvez en toute sécurité et en utilisant la saisie semi-automatique de Visual Studio inclure ou exclure des champs.

  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
la source
1

Installez simplement ExpectedObjects à partir de Nuget, vous pouvez facilement comparer la valeur de propriété de deux objets, chaque valeur d'objet de la collection, la valeur de deux objets composés et la valeur de propriété de comparaison partielle par type anonyme.

J'ai quelques exemples sur github: https://github.com/hatelove/CompareObjectEquals

Voici quelques exemples contenant des scénarios de comparaison d'objets:

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

Référence:

  1. ExpectedObjects github
  2. Introduction des objets attendus
En91
la source
1

J'ai fini par écrire une simple fabrique d'expressions:

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

et utilisez-le simplement:

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

C'est très utile car je dois comparer la collection de tels objets. Et vous pouvez utiliser ce comparateur ailleurs :)

Voici l'essentiel de l'exemple: https://gist.github.com/Pzixel/b63fea074864892f9aba8ffde312094f

Alex Zhukovskiy
la source
0

Désérialisez les deux classes et effectuez une comparaison de chaînes.

EDIT: Fonctionne parfaitement, c'est la sortie que j'obtiens 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)

MODIFIER DEUX: Les deux objets peuvent être identiques, mais l'ordre dans lequel les propriétés sont sérialisées n'est pas le même. Par conséquent, le XML est différent. DOH!

EDIT TROIS: Cela fonctionne. Je l'utilise dans mes tests. Mais vous devez ajouter des éléments aux propriétés de la collection dans l'ordre dans lequel le code testé les ajoute.

Casey Burns
la source
1
sérialiser ? Idée intéressante. Je ne suis pas sûr de savoir comment cela tiendrait en termes de performances, cependant
Michael Haren
ne vous permettra pas de comparer des doubles ou des décimales avec une précision donnée.
Noctis
0

Je sais que c'est une question très ancienne, mais NUnit n'a toujours pas de support natif pour cela. Cependant, si vous aimez les tests de style BDD (ala Jasmine), vous serez agréablement surpris par NExpect ( https://github.com/fluffynuts/NExpect , obtenez-le de NuGet), qui a des tests d'égalité profonds cuits ici .

(avertissement: je suis l'auteur de NExpect)

daf
la source
-1

Stringifier et comparer deux chaînes

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

jmtt89
la source
-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
la source
Merci pour cet extrait de code, qui pourrait fournir une aide limitée à court terme. Une explication appropriée améliorerait considérablement sa valeur à long terme en montrant pourquoi c'est une bonne solution au problème, et la rendrait plus utile aux futurs lecteurs avec d'autres questions similaires. Veuillez modifier votre réponse pour ajouter des explications, y compris les hypothèses que vous avez formulées.
Toby Speight le
Et qu'est-ce que cela ajoute à la réponse de Max ?
Toby Speight le