Почему тест "Assert.AreEqual" провалился, когда я сравниваю два пустых списка?

У меня есть класс MyCustomClass:

public MyCustomClass
{
    public MyCustomClass()
    {
        MyObject = new List<MyCustomObject>();
    }

    public List<MyCustomObject> MyObject {get; set;}
}

В тесте:

List<MyCustomObject> aux = new List<MyCustomObject>();
MyCustomClass oClass = new MyCustomClass();
Assert.AreEqual(aux, oClass.MyObject)

Тест провалился, почему? Каждое свойство, статический член и т.д. Одинаковы.

Ответ 1

В этом случае Assert.AreEqual проверит, совпадают ли эти два объекта, а они нет. Вместо этого вы должны использовать CollectionAssert.AreEqual, который вернет true, если два "имеют одинаковые элементы в одном порядке и количестве".

Ответ 2

Поскольку уже ответил, два списка одного типа с нулевыми элементами не считаются равными.

Причина этого заключается в том, что AreEqual на самом деле вызывает aux.AreEqual(oClass.MyObject), используя реализацию собственного значения объектов. Поскольку это не переопределено для List<T>, оно возвращается к реализации в Object, что является простой контрольной проверкой равенства. Эти два списка явно не являются одной и той же ссылкой, и поэтому они не считаются равными.

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

Ответ 3

Я декомпилировал Assert.AreEqual (который находится в сборке Microsoft.VisualStudio.QualityTools.UnitTestFramework GAC), используя dotPeek и обнаружил, что Assert.AreEqual(aux, oClass.MyObject) в конечном итоге приведет к следующий вызов, где aux - expected, а oClass.MyObject - actual:

object.Equals((object) expected, (object) actual)

Из документации для статического object.Equals(Object, Object) мы читаем:

Метод статических эквивалентов (объект, объект) указывает, являются ли два объекты, objA и objB, равны. Он также позволяет тестировать объекты значение которого равно null. Он сравнивает objA и objB для равенство:

Определяет, представляют ли оба объекта один и тот же объект Справка. Если это так, метод возвращает true. Этот тест эквивалентно вызову метода ReferenceEquals. Кроме того, если оба objA и objB равны null, метод возвращает true.

Определяет, является ли objA или objB нулевым. Если да, то возвращается ложь.

Если два объекта не представляют одну и ту же ссылку на объект и none равно null, он вызывает objA.Equals(objB) и возвращает результат. Это означает, что если objA переопределяет метод Object.Equals(Object) это переопределение вызывается.

Теперь List<T> известен как ссылочный тип, и мы знаем, что ни один из двух списков, которые вы сравниваете, не имеет значения, поэтому окончательное сравнение между вашими двумя объектами будет

expected.Equals(actual)

Так как List<T> не переопределяет Equals, он использует реализацию базового объекта, которая выполняет ссылочное сравнение и, таким образом, терпит неудачу (expected и actual "были добавлены отдельно).

То, что вы хотите, это структурное сравнение, т.е. парное равенство элементов в ваших списках. См. Ответ @ReedCopsey для правильного утверждения для этого (CollectionAssert.AreEqual).