XUnit Assertion для проверки равенства объектов

Я использую платформу XUnit для тестирования своего кода C #.

Есть ли в этой структуре доступный метод assert, который выполняет сравнение объектов? Я намерен проверить равенство всех публичных и частных переменных-членов объекта.

Я пробовал эти альтернативы, но они редко срабатывают:

1) bool IsEqual = (Obj1 == Obj2)
2) Assert.Same(Obj1, Obj2) which I couldnt understand what happens internally

person inquisitive    schedule 21.06.2012    source источник
comment
В xUnit есть глубокое сравнение. Вам нужно будет реализовать IEquatable ‹T› для своих объектов, и тогда Assert.Equals будет работать.   -  person seldary    schedule 21.06.2012
comment
Assert.Same() сравнивает по ссылке; он утверждает, что Obj1 и Obj2 являются одним и тем же объектом, а не просто выглядят одинаково.   -  person TeaDrivenDev    schedule 12.05.2014


Ответы (5)


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

Assert.True(obj1.Equals(obj2));

Вот страница MSDN с перегрузкой метода Equals abt: http://msdn.microsoft.com/en-us/library/ms173147(v=vs.80).aspx

Также добавьте комментарий к вопросу: В чем разница между IEquatable и просто переопределить Object.Equals ()?

person Baz1nga    schedule 21.06.2012
comment
Я понимаю, что эту проверку можно выполнить, реализовав собственный метод Equals. Но есть ли какой-нибудь метод слепого сравнения байтов, который упростит проверку? Это потому, что в конечном итоге у меня будет тестируемая реализация Equals в программном обеспечении только для модульного тестирования. - person inquisitive; 21.06.2012
comment
Я попытался использовать методы сериализации для преобразования обоих объектов в байтовый массив, и это сработало. Но это связано с ограничением добавления атрибута [serializable] в мой класс, который имеет частные переменные-члены. Я думаю, это нехорошо с точки зрения дизайна - person inquisitive; 21.06.2012
comment
Я против переопределения этих двух методов только для модульных тестов. - person maracuja-juice; 23.01.2019
comment
А что произойдет, если ваша логика равенства бизнес-правил отличается от логики проверки равенства? - person Ian Kemp; 14.10.2019
comment
вам просто нужно передать IEqualityComparer в качестве третьего аргумента `Assert.Equal (expectedCar, actualCar, CarComparer); ` - person Poat; 07.04.2020
comment
Этот ответ полезен только до тех пор, пока работает ссылка (а она больше не работает). - person Marc Levesque; 21.09.2020

У меня была аналогичная проблема, но, к счастью, я уже использую

using Newtonsoft.Json;

Поэтому мне просто нужно было сериализовать его в объект json, а затем сравнить как строку.

var obj1Str = JsonConvert.SerializeObject(obj1);
var obj2Str = JsonConvert.SerializeObject(obj2);
Assert.Equal(obj1Str, obj2Str );
person The Integrator    schedule 02.05.2017
comment
Я считаю это более полезным, чем реализация метода Equals, потому что я хотел бы, чтобы моя ошибка assert сообщала мне что-то о том, что было не так. В идеале я бы хотел иметь что-то, что могло бы проходить по дереву объектов и накапливать информацию о том, какие свойства / поддеревья не равны и терпят неудачу с этой информацией. - person Rikki Gibson; 16.12.2017
comment
@RikkiGibson не реализует Equals как раз правильно? Просто добавьте исключения в список на равных для каждого свойства, которое отличается, а затем в конце либо верните true, либо выбросите все исключения. - person Fabio Lolli; 17.01.2018
comment
Да, но делать это от случая к случаю может быть непросто, поэтому в прошлом я, как правило, искал решения, основанные на отражении, при сравнении деревьев простых старых объектов, примитивов и коллекций. - person Rikki Gibson; 17.01.2018
comment
@RikkiGibson Есть несколько пакетов NuGet, которые делают то, что вы хотите. Смотрите мой ответ. - person maracuja-juice; 12.09.2018

Библиотека FluentAssertions имеет внутри довольно мощную логику сравнения.

myObject.ShouldBeEquivalentTo(new { SomeProperty = "abc", SomeOtherProperty = 23 });

Вы даже можете использовать это для утверждения в части «myObject». Однако это может не помочь вам с частными полями.

person Adrian Petrescu    schedule 29.08.2019
comment
Текущая версия библиотеки, похоже, использует следующий API: actual.Should().BeEquivalentTo(expected); - person petrsyn; 22.02.2021
comment
Я выползаю из-под своего камня и сталкиваюсь с тестовым монстром xUnit, которого я должен ... В этом так мало смысла, - говорю я. Моему внутреннему ребенку-программисту всегда приходилось напоминать, что истерики юнит-тестов совершенно не соответствуют возрасту. Что ж, это было до того, как я открыл для себя FluentAssertions. Тот день был сегодня. И вы, @AdrianPetrescu, искренне цените этого разработчика с затуманенными глазами. - person IdusOrtus; 14.05.2021

Есть пакеты NuGet, которые делают это за вас. Вот два примера, которые я использую лично.

  1. DeepEqual:

    object1.ShouldDeepEqual(object2);
    
  2. ExpectedObjects:

    [Fact]
    public void RetrievingACustomer_ShouldReturnTheExpectedCustomer()
    {
      // Arrange
      var expectedCustomer = new Customer
      {
        FirstName = "Silence",
        LastName = "Dogood",
        Address = new Address
        {
          AddressLineOne = "The New-England Courant",
          AddressLineTwo = "3 Queen Street",
          City = "Boston",
          State = "MA",
          PostalCode = "02114"
        }                                            
      }.ToExpectedObject();
    
    
      // Act
      var actualCustomer = new CustomerService().GetCustomerByName("Silence", "Dogood");
    
      // Assert
      expectedCustomer.ShouldEqual(actualCustomer);
    }
    
person maracuja-juice    schedule 17.08.2018
comment
Есть какие-нибудь мнения о плюсах и минусах этих библиотек? - person Will P.; 02.11.2018
comment
@WillP. DeepEqual не имеет официальной поддержки .NET Standard / Core (пока). Это работает, но может вызвать проблемы. В остальном они почти такие же. ExpectedObjects имеет еще несколько функций, таких как частичное или настраиваемое сравнение. Вот почему я использую ExpectedObjects практически во всех своих проектах в настоящее время, но это больше личное предпочтение. - person maracuja-juice; 05.11.2018

Я знаю, что это старый вопрос, но поскольку я наткнулся на него, я подумал, что взвесу новое доступное решение (по крайней мере, в xunit 2.3.1 в решении .net Core 2.0).

Я не уверен, когда это было введено, но теперь существует перегруженная форма .Equal, которая принимает экземпляр IEqualityComparer<T> в качестве третьего параметра. Вы можете создать собственный компаратор в своем модульном тесте, не загрязняя им свой код.

Следующий код можно вызвать следующим образом: Assert.Equal(expectedParameters, parameters, new CustomComparer<ParameterValue>());

Похоже, что XUnit по умолчанию прекращает обработку теста, как только обнаруживается сбой, поэтому запуск нового EqualException из нашего компаратора, похоже, соответствует тому, как XUnit работает из коробки.

public class CustomComparer<T> : IEqualityComparer<T>
{
    public bool Equals(T expected, T actual)
    {
        var props = typeof(T).GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
        foreach (var prop in props)
        {
            var expectedValue = prop.GetValue(expected, null);
            var actualValue = prop.GetValue(actual, null);
            if (!expectedValue.Equals(actualValue))
            {
                throw new EqualException($"A value of \"{expectedValue}\" for property \"{prop.Name}\"",
                    $"A value of \"{actualValue}\" for property \"{prop.Name}\"");
            }
        }

        return true;
    }

    public int GetHashCode(T parameterValue)
    {
        return Tuple.Create(parameterValue).GetHashCode();
    }
}

Изменить: я обнаружил, что сравнение фактических и ожидаемых значений с != не было эффективным для определенных типов (я уверен, что есть лучшее объяснение, связанное с различием между ссылочными типами и типами значений, но это не на сегодня). Я обновил код, чтобы использовать метод .Equals для сравнения двух значений, и, похоже, он работает намного лучше.

person Engineer_Andrew    schedule 13.04.2018
comment
Это работает, но я бы предпочел просто использовать пакет NuGet, который делает то же самое. Меньше кода, о котором мне нужно беспокоиться - person maracuja-juice; 12.09.2018
comment
Вы не должны throw на Equals. Он может облажаться Assert.DoesNotContain, поскольку ожидает получить ложное значение для каждой записи. Лучше верни false. - person Adarsha; 18.05.2020
comment
@ maracuja-juice действительно меньше кода, но зависимость от внешнего пакета и все связанные с ним кибер-риски ... - person Necriis; 19.11.2020
comment
Поскольку этот ответ продолжает получать голоса более трех лет спустя, я продолжу и отмечу, что для этого я создал пакет NuGet с открытым исходным кодом, и он намного надежнее того, что я здесь включил. Источник: пакет github.com/engineer-andrew/ea-xunit-helpers : nuget.org/packages/EAXUnitHelpers.Comparison - person Engineer_Andrew; 01.07.2021