Порівняйте рівність між двома об’єктами в NUnit


126

Я намагаюся стверджувати, що один об’єкт "дорівнює" іншому.

Об'єкти - це лише екземпляри класу з купою загальнодоступних властивостей. Чи існує простий спосіб встановити рівність NUnit на основі властивостей?

Це моє поточне рішення, але я думаю, що може бути щось краще:

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

Те, що я збираюся, було б у тому ж дусі, що і CollectionEquivalentConstraint, де NUnit підтверджує, що вміст двох колекцій однаковий.

Відповіді:


51

Переопределить .Equals для вашого об'єкта, і в тесті одиниці ви можете просто зробити це:

Assert.AreEqual(LeftObject, RightObject);

Звичайно, це може означати, що ви просто перемістіть усі індивідуальні порівняння до методу .Equals, але це дозволить вам повторно використовувати цю реалізацію для декількох тестів, і, мабуть, має сенс мати, якщо об’єкти в будь-якому разі зможуть порівнювати себе з побратимами.


2
Спасибі, лассевк. Це працювало для мене! Я реалізував .Equals відповідно до інструкцій тут: msdn.microsoft.com/en-us/library/336aedhh(VS.80).aspx
Майкл Харен

12
І GetHashCode (), очевидно
;-p

Число 1 у списку на цій сторінці означає переосмислення GetHashCode, і він сказав, що він дотримується цих вказівок :) Але так, поширена помилка ігнорувати це. Як правило, це не помилка, яку ви помічаєте більшу частину часу, але коли ви це зробите, це як один з тих часів, коли ви говорите "Ой, ей, чому це змія мені штани і чому він кусає мою дупу".
Лассе В. Карлсен

1
Одне важливе застереження: якщо ваш об’єкт також реалізує, IEnumerableйого порівнюватимуть як колекцію незалежно від переважних реалізацій, Equalsоскільки NUnit надає IEnumerableбільшу перевагу. NUnitEqualityComparer.AreEqualДетально див. Методи. Можна порівняти порівняння, скориставшись одним із Using()методів обмеження рівності . Навіть тоді не достатньо реалізувати не загальну IEqualityComparerчерез адаптер, який використовує NUnit.
Калеб Педерсон

13
Більше Caveat: Реалізація GetHashCode()на змінних типах буде погано поводитися, якщо ви коли-небудь використовуватимете цей об'єкт як ключ. ІМХО, перекриваючи Equals(), GetHashCode()і роблять об'єкт непорушним тільки для тестування не має сенсу.
bavaza

118

Якщо з будь-якої причини ви не можете змінити рівність, ви можете побудувати допоміжний метод, який повторює загальнодоступні властивості шляхом відображення та затвердження кожної властивості. Щось на зразок цього:

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: це неправда. Метод Type.GetProperties: Повертає всі загальнодоступні властивості поточного типу. Дивіться msdn.microsoft.com/en-us/library/aky14axb.aspx
Сергій Волчков

4
Дякую. однак мені довелося переключити порядок фактичних та очікуваних парам, оскільки конверсія - це те, що очікуване є парам до фактичного.
Валама

це кращий підхід IMHO, переопрацювання рівних та HashCode не повинні базуватися на порівнянні кожного поля і плюс, що дуже нудно робити для кожного об'єкта. Хороша робота!
Скотт Уайт

3
Це чудово працює, якщо ваш тип має лише основні типи як властивості. Однак якщо ваш тип має властивості зі спеціальними типами (які не реалізують рівність), він не вдасться.
Боббі Кеннон

Додано деяку рекурсію для властивостей об’єкта, але мені довелося пропустити індексовані властивості:
cerhart

113

Не перевищуйте рівних лише для тестування. Це нудно і впливає на логіку домену. Натомість

Використовуйте JSON для порівняння даних об'єкта

Ніякої додаткової логіки на ваших об'єктах. Немає зайвих завдань для тестування.

Просто використовуйте цей простий метод:

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

Здається, це виходить чудово. Інформація про результати тестувального бігуна покаже порівняння рядків JSON (об’єктний графік), щоб ви побачили, що неправильно.

Також зверніть увагу! Якщо у вас є більші складні об'єкти і ви просто хочете порівняти їх частини, ви можете ( використовувати LINQ для даних послідовностей ) створити анонімні об'єкти для використання з вищевказаним методом.

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
Це відмінний спосіб перевірити, особливо якщо ви все-таки маєте справу з JSON (наприклад, використовуєте набраний клієнт для доступу до веб-служби). Ця відповідь повинна бути набагато вищою.
Roopesh Shenoy

1
Використовуйте Linq! @DmitryBLR (див. Останній абзац у відповідь) :)
Макс

3
Це чудова ідея. Я б використовував новіший Json.NET: var очікуєтьсяJson = Newtonsoft.Json.JsonConvert.SerializeObject (очікується);
BrokeMyLegBiking

2
Це не буде працювати з круговими посиланнями. Використовуйте замість github.com/kbilsted/StatePrinter для покращення досвіду щодо підходу JSON
Carlo V. Dango

2
Це правда @KokaChernov, і іноді ви хочете провалити тест, якщо не замовлення однакове, але якщо ви не хочете провалитись, якщо замовлення не те саме, ви можете зробити явний сортування (використовуючи linq) у списках, перш ніж передати їх методу AreEqualByJson. Простий варіант "перестановки" ваших об'єктів перед тестуванням - в останньому прикладі коду у відповіді. Так що я думаю, що це дуже "універсально"! :)
Макс

91

Спробуйте бібліотеку FluentAssertions:

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

http://www.fluentassertions.com/

Його також можна встановити за допомогою NuGet.


18
Якщо він був застарілим, так має бути dto.ShouldBeEquivalentTo (клієнт); натомість
WhiteKnight

2
Це найкраща відповідь з цієї причини .
Тодд Меньє

ShouldBeEquivalent is buggy :(
Костянтин Чернов

3
просто була та сама проблема, і використав наступну, яка, здається, працює нормально:actual.ShouldBeEquivalentTo(expected, x => x.ExcludingMissingMembers())
stt106

1
Це чудова лайка! Не вимагає перевизначення рівних, а також (якщо рівнозначне перевищення рівняння, наприклад, для об'єктів цінності) не покладається на правильну реалізацію. Також різниця друкується добре, як Hamcrest робить для Java.
кап

35

Я вважаю за краще не перевищувати рівність, щоб увімкнути тестування. Не забувайте, що якщо ви переорієнтуєтесь на "рівне", вам також слід перекрити GetHashCode, або ви можете отримати несподівані результати, наприклад, якщо ви використовуєте об'єкти в словнику.

Мені подобається підхід до роздумів вище, оскільки він потребує додавання властивостей у майбутньому.

Для швидкого та простого рішення, але найчастіше найпростіше створити допоміжний метод, який перевіряє, чи об'єкти рівні, або впровадити IEqualityComparer у класі, який ви залишаєте приватним для своїх тестів. Використовуючи рішення IEqualityComparer, вам не потрібно займатись реалізацією GetHashCode. Наприклад:

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

Рівень не обробляє нульові значення. Я додам наступне перед вашим твердженням повернення методом рівних. if (x == null && y == null) {return true; } if (x == null || y == null) {return false; } Я редагував питання, щоб додати нульову підтримку.
Боббі Кеннон

Не працює для мене з кидком нового NotImplementedException (); у коді GetHashCode. Навіщо мені потрібна ця функція в IEqualityComparer в будь-якому випадку?
love2code

15

Я спробував кілька згаданих тут підходів. Більшість з них передбачає серіалізацію об'єктів і порівняння рядків. Хоча це дуже просто і, як правило, дуже ефективно, я виявив, що виникає трохи короткий час, коли у вас виникає збій, і щось подібне отримує:

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

Зрозуміти, де є відмінності, болісно сказати.

Завдяки порівнянню об'єктних графіків об'єктів FluentAssertions (тобто a.ShouldBeEquivalentTo(b)) ви отримуєте це назад:

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

Це набагато приємніше. Отримайте FluentAssertions зараз, ви будете раді пізніше (і якщо ви підтримаєте це, будь ласка, підкажіть відповідь dkl там, де вперше було запропоновано FluentAssertions).


9

Я погоджуюся з ChrisYoxall - реалізація рівнянь у вашому головному коді виключно для тестування не є доброю.

Якщо ви реалізуєте рівне, тому що певна логіка програми вимагає цього, то це нормально, але чистий код лише для тестування не переймається елементами (також семантика перевірки того ж тестування може відрізнятися від того, що вимагає ваша програма).

Коротше кажучи, не тестуйте код лише у своєму класі.

Просте неглибоке порівняння властивостей за допомогою відображення повинно вистачити для більшості класів, хоча, можливо, вам доведеться повторити, якщо ваші об'єкти мають складні властивості. Якщо дотримуйтесь посилань, остерігайтеся кругових посилань чи подібних.

Хитрий


Гарний вилов на кругових посиланнях. Легко подолати, якщо ви зберігаєте словник об’єктів, які вже є у дереві порівняння.
Лукас Б

6

Обмеження властивостей , додані в NUnit 2.4.2, дозволяють зрозуміти рішення, яке можна прочитати більше, ніж оригінальне, і воно створює набагато кращі повідомлення про помилки. Це ні в якому разі не є загальним, але якщо вам не потрібно робити це для занадто багатьох класів, це дуже адекватне рішення.

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

Не настільки загального призначення, як реалізація, Equalsале це дає набагато краще повідомлення про помилку, ніж

Assert.AreEqual(ExpectedObject, ActualObject);

4

Рішення Макса Вікстрома JSON (вище) має для мене найбільш сенс, він короткий, чистий і головне, що він працює. Особисто я хотів би скористатися перетворенням JSON в якості окремого методу і помістити твердження назад всередині тесту одиниці, як це ...

МЕТОД ДОПОМОГИ:

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

ТЕСТУВАННЯ ОДИН:

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 - Вам може знадобитися додати посилання на System.Web.Extensions у своєму рішенні.


4

Це досить стара нитку , але мені було цікаво , якщо є причина , чому немає відповіді запропонував NUnit.Framework.Is.EqualToіNUnit.Framework.Is.NotEqualTo ?

Як от:

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

і

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

4
Оскільки він не роздруковує деталі, що відрізняється
Шраг Сміловіц

1

Інший варіант - написати спеціальне обмеження, реалізуючи абстрактний Constraintклас NUnit . Завдяки класу помічників, щоб забезпечити трохи синтаксичного цукру, отриманий тестовий код є приємно коротким і читабельним, наприклад

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

Для крайнього прикладу розглянемо клас, у якого є члени "лише для читання", це не так IEquatable, і ти не зміг змінити тестований клас, навіть якщо хотів:

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

Контракт для Constraintкласу вимагає переосмислення Matchesта WriteDescriptionTo(у випадку невідповідності, розповіді на очікувану цінність), а й переосмислення WriteActualValueTo(розповідь про фактичну цінність) має сенс:

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

Плюс клас помічників:

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

Приклад використання:

[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

Я б спирався на відповідь @Juanma. Однак я вважаю, що це не повинно реалізовуватися з твердженнями одиничних тестів. Це утиліта, яка дуже добре може бути використана в деяких умовах нетестовим кодом.

Я написав статтю з цього питання http://timoch.com/blog/2013/06/unit-test-equality-is-not-domain-equality/

Моя пропозиція така:

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

Використовуючи це за допомогою NUnit

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

приводить таке повідомлення про невідповідність.

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 було написано спеціально для скидання об'єктних графіків на представлення рядків з метою написання простих одиничних тестів.

  • Це методи з методами Assert, які виводять правильно тестований рядок і просто копіюють його в тест, щоб виправити його.
  • Це дозволяє автоматично переписати unittest
  • Він інтегрується з усіма рамками тестування одиниць
  • На відміну від серіалізації JSON, кругові посилання підтримуються
  • Ви можете легко фільтрувати, тому скидаються лише частини типів

Дано

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

Ви можете безпечним способом і, використовуючи автоматичне завершення візуальної студії, включати або виключати поля.

  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

Просто встановіть ExposedObjects з Nuget, ви можете легко порівняти значення властивостей двох об'єктів, кожне значення колекції об'єкта, два складених об'єкта і часткове порівняння значення властивості за анонімним типом.

У мене є кілька прикладів щодо github: https://github.com/hatelove/CompareObjectEquals

Ось кілька прикладів, які містять сценарії порівняння об'єкта:

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

Довідка:

  1. Очікувані об'єкти github
  2. Введення очікуваних об'єктів


1

Я закінчив писати просту фабрику виразів:

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

і просто використовуйте його:

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

Це дуже корисно, оскільки мені доводиться порівнювати колекцію таких предметів. І ви можете скористатися цим порівнянням де-небудь ще :)

Ось істота з прикладом: https://gist.github.com/Pzixel/b63fea074864892f9aba8ffde312094f


0

Десеріалізуйте обидва класи та порівняйте рядки.

EDIT: прекрасно працює, це результат, який я отримую від 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)

Редагувати два: два об'єкти можуть бути однаковими, але порядок, за яким властивості серіалізуються, неоднаковий. Тому XML відрізняється. ДОХ!

РЕДАКТИРУЙТЕ ТРИ: Це дійсно працює. Я використовую це у своїх тестах. Але ви повинні додати елементи до властивостей колекції у порядку, до якого додається тестований код.


1
серіалізувати ? Цікава ідея. Я не впевнений, як це витримає з точки зору продуктивності, проте
Майкл Харен

не дозволить порівнювати парні чи десяткові знаки з заданою точністю.
Noctis

0

Я знаю, що це дійсно давнє питання, але NUnit досі не має нашої підтримки для цього. Однак якщо вам подобається тестування в стилі BDD (ала Жасмін), ви будете приємно здивовані NExpect ( https://github.com/fluffynuts/NExpect , отримайте його від NuGet), в якому випробовано глибоке тестування рівності. .

(відмова від відповідальності: я автор NExpect)


-1

Струфікуйте та порівняйте два рядки

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

Дякуємо за цей фрагмент коду, який може надати деяку короткочасну допомогу. Правильне пояснення значно покращило б його довгострокове значення, показавши, чому це хороше рішення проблеми, та зробило б кориснішим майбутнім читачам інші подібні питання. Будь ласка, відредагуйте свою відповідь, щоб додати пояснення, включаючи зроблені вами припущення.
Toby Speight

І що це додає до відповіді Макса ?
Toby Speight
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.