Bezdepbonusu7Программирование: статьи и ответы

Вопрос

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

Автор вопроса: inquisitiveОтветов: 5

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

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

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

1) bool IsEqual = (Obj1 == Obj2)
2) Assert.Same(Obj1, Obj2) which I couldnt understand what happens internally
  • В xUnit есть глубокое сравнение. Вам нужно будет реализовать IEquatable ‹T› для своих объектов, и тогда Assert.Equals будет работать. seldary 21.06.2012
  • Assert.Same() сравнивает по ссылке; он утверждает, что Obj1 и Obj2 являются одним и тем же объектом, а не просто выглядят одинаково. TeaDrivenDev 12.05.2014

xunit

Ответы

Ответ 1 · рейтинг 19 · Baz1nga

Для этого вам понадобится настраиваемый компаратор, когда вы сравниваете объекты, в противном случае они проверяются на основе того, ссылаются ли они на один и тот же объект в памяти. Чтобы переопределить это поведение, вам нужно переопределить методы 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 ()?

Ответ 2 · рейтинг 79 · The Integrator

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

using Newtonsoft.Json;

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

var obj1Str = JsonConvert.SerializeObject(obj1);
var obj2Str = JsonConvert.SerializeObject(obj2);
Assert.Equal(obj1Str, obj2Str );

Ответ 3 · рейтинг 18 · Adrian Petrescu

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

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

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

Ответ 4 · рейтинг 16 · maracuja-juice

Есть пакеты 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);
    }
    

Ответ 5 · рейтинг 9 · Engineer_Andrew

Я знаю, что это старый вопрос, но поскольку я наткнулся на него, я подумал, что взвесу новое доступное решение (по крайней мере, в 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 для сравнения двух значений, и, похоже, он работает намного лучше.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы