Сравните равенство между двумя объектами в 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
Еще одно предостережение: реализация GetHashCode()изменяемых типов приведет к неправильному поведению, если вы когда-либо будете использовать этот объект в качестве ключа. ИМХО, перекрывая Equals(), GetHashCode()и делают объект непреложным только для тестирования не имеет смысла.
bavaza
118

Если вы не можете переопределить Equals по какой-либо причине, вы можете создать вспомогательный метод, который выполняет итерацию по общедоступным свойствам путем отражения и утверждает каждое свойство. Что-то вроде этого:

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
источник
@ Уэсли: это неправда. Метод Type.GetProperties: возвращает все общедоступные свойства текущего типа. См. Msdn.microsoft.com/en-us/library/aky14axb.aspx
Сергей Волчков,
4
Спасибо. однако мне пришлось поменять порядок фактических и ожидаемых параметров, поскольку преобразование состоит в том, что ожидаемый параметр предшествует фактическому.
Valamas
это лучший подход. ИМХО, переопределения Equal и HashCode не должны основываться на сравнении каждого поля, плюс это очень утомительно для каждого объекта. Хорошая работа!
Скотт Уайт
3
Это отлично работает, если ваш тип имеет только базовые типы в качестве свойств. Однако, если у вашего типа есть свойства с настраиваемыми типами (которые не реализуют Equals), он завершится ошибкой.
Бобби Кэннон,
Добавлена ​​рекурсия для свойств объекта, но мне пришлось пропустить индексированные свойства:
cerhart
113

Не переопределяйте Equals только в целях тестирования. Это утомительно и влияет на логику предметной области. Вместо,

Используйте 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 (например, используете типизированный клиент для доступа к веб-сервису). Этот ответ должен быть намного выше.
Рупеш Шеной
1
Используйте Linq! @DmitryBLR (см. Последний абзац в ответе) :)
Max
3
Это отличная идея. Я бы использовал более новый Json.NET: var expectedJson = Newtonsoft.Json.JsonConvert.SerializeObject (expected);
BrokeMyLegBiking
2
Это не будет работать с круговыми ссылками. Вместо этого используйте github.com/kbilsted/StatePrinter, чтобы улучшить работу с JSON
Карло В. Данго,
2
Это правда @KokaChernov, и иногда вы хотите провалить тест, если порядок не такой же, но если вы не хотите терпеть неудачу, если порядок не такой же, вы можете сделать явную сортировку (используя linq) в списках, прежде чем передавать их в метод AreEqualByJson. Простой вариант «перегруппировки» ваших объектов перед тестированием - в последнем примере кода в ответе. Думаю, это очень универсально! :)
Макс
91

Попробуйте библиотеку FluentAssertions:

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

http://www.fluentassertions.com/

Его также можно установить с помощью NuGet.

декалитр
источник
18
ShouldHave устарел, поэтому должен быть dto.ShouldBeEquivalentTo (customer); вместо этого
WhiteKnight
2
По этой причине это лучший ответ .
Тодд Менье
ShouldBeEquivalent глючит :(
Константин Чернов
3
у меня была такая же проблема, и я использовал следующее, которое, похоже, работает нормально:actual.ShouldBeEquivalentTo(expected, x => x.ExcludingMissingMembers())
stt106
1
Это отличная библиотека! Не требует переопределения Equals, а также (если равенство все равно переопределяется, например, для объектов значений) не зависит от правильной реализации. Кроме того, разница отображается красиво, как Hamcrest для Java.
кап
35

Я предпочитаю не переопределять Equals только для включения тестирования. Не забывайте, что если вы переопределяете Equals, вам действительно следует также переопределить 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.");
    }
}
Крис Йоксалл
источник
Equals не обрабатывает нулевые значения. Я бы добавил следующее перед вашим оператором return в методе equals. if (x == null && y == null) {вернуть истину; } if (x == null || y == null) {return false; } Я отредактировал вопрос, чтобы добавить нулевую поддержку.
Бобби Кэннон,
У меня не работает throw new 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 - реализация Equals в вашем основном коде исключительно для целей тестирования нехороша.

Если вы реализуете Equals, потому что этого требует какая-то логика приложения, тогда это нормально, но не загромождайте чистый код, предназначенный только для тестирования (также семантика проверки того же самого для тестирования может отличаться от того, что требуется вашему приложению).

Короче говоря, держите код, предназначенный только для тестирования, вне вашего класса.

Простого неглубокого сравнения свойств с использованием отражения должно быть достаточно для большинства классов, хотя вам может потребоваться рекурсия, если ваши объекты имеют сложные свойства. Если вы следуете ссылкам, остерегайтесь циклических ссылок или аналогичных.

лукавый

Хитрый грифон
источник
Хорошая уловка по круговым ссылкам. Легко преодолеть, если вы держите словарь объектов уже в дереве сравнения.
Lucas B
6

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

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

Решение Max Wikstrom JSON (см. Выше) имеет для меня наибольший смысл, оно короткое, чистое и, что самое главное, работает. Лично я бы предпочел реализовать преобразование JSON как отдельный метод и поместить assert обратно в модульный тест следующим образом ...

ПОМОЩЬ:

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

К вашему сведению - вам может потребоваться добавить ссылку на System.Web.Extensions в ваше решение.

samaspin
источник
4

Это довольно старая ветка, но мне было интересно, есть ли причина, по которой не предлагается ответ NUnit.Framework.Is.EqualToи NUnit.Framework.Is.NotEqualTo?

Такие как:

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

и

Assert.That(LeftObject, Is.Not.EqualTo(RightObject)); 
user2315856
источник
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 ) );
    }
}
onedaywhen
источник
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
TiMoch
источник
1

https://github.com/kbilsted/StatePrinter был написан специально для вывода графов объектов в строковое представление с целью написания простых модульных тестов.

  • Он поставляется с методами Assert, которые выводят правильно экранированную строку, которую можно легко скопировать и вставить в тест, чтобы исправить ее.
  • Это позволяет автоматически переписывать unittest
  • Он интегрируется со всеми фреймворками модульного тестирования.
  • В отличие от сериализации JSON, поддерживаются циклические ссылки.
  • Вы можете легко фильтровать, поэтому сбрасываются только части типов

Дано

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

Вы можете типобезопасным способом и с помощью автозаполнения Visual Studio включать или исключать поля.

  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

Просто установите ExpectedObjects из 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. ExpectedObjects github
  2. Введение ожидаемых объектов
In91
источник
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

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

РЕДАКТИРОВАТЬ: отлично работает, это результат, который я получаю от 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 другой. DOH!

ИЗМЕНИТЬ ТРИ: это работает. Я использую его в своих тестах. Но вы должны добавлять элементы в свойства коллекции в том порядке, в котором их добавляет тестируемый код.

Кейси Бернс
источник
1
сериализовать ? Интересная идея. Я не уверен, насколько это будет выглядеть с точки зрения производительности,
Майкл Харен
не позволит вам сравнивать числа с двойной или десятичной дробью с заданной точностью.
Ноктис
0

Я знаю, что это действительно старый вопрос, но NUnit до сих пор не имеет встроенной поддержки для этого. Однако, если вам нравится тестирование в стиле BDD (ala Jasmine), вы были бы приятно удивлены NExpect ( https://github.com/fluffynuts/NExpect , получите его из NuGet), в котором есть глубокое тестирование равенства. ,

(отказ от ответственности: я являюсь автором NExpect)

ДАФ
источник
-1

Стренить и сравнить две строки

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

jmtt89
источник
-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);
}
Сатиш Бабу
источник
Спасибо за этот фрагмент кода, который может оказать некоторую краткосрочную помощь. Правильное объяснение значительно повысило бы его ценность в долгосрочной перспективе, показав, почему это хорошее решение проблемы, и сделало бы его более полезным для будущих читателей с другими похожими вопросами. Пожалуйста , измените свой ответ , чтобы добавить некоторые объяснения, в том числе допущений , которые вы сделали.
Тоби Спейт,
И что это добавляет к ответу Макса ?
Тоби Спейт,