С# - Утверждение двух объектов одинаково в модульных тестах

Использование Nunit или Microsoft.VisualStudio.TestTools.UnitTesting. Сейчас мое утверждение терпит неудачу.

    [TestMethod]
    public void GivenEmptyBoardExpectEmptyBoard()
    {
        var test = new Board();

        var input = new Board()
            {
                Rows = new List<Row>()
                    {
                        new Row(){Cells = new List<int>(){0,0,0,0}},
                        new Row(){Cells = new List<int>(){0,0,0,0}},
                        new Row(){Cells = new List<int>(){0,0,0,0}},
                        new Row(){Cells = new List<int>(){0,0,0,0}},
                    }
            };

        var expected = new Board()
        {
            Rows = new List<Row>()
                    {
                        new Row(){Cells = new List<int>(){0,0,0,0}},
                        new Row(){Cells = new List<int>(){0,0,0,0}},
                        new Row(){Cells = new List<int>(){0,0,0,0}},
                        new Row(){Cells = new List<int>(){0,0,0,0}},
                    }
        };

        var lifeOrchestration = new LifeOrchestration();

        var actual = lifeOrchestration.Evolve(input);

        Assert.AreEqual(expected, actual);
    }

Ответ 1

У вас есть два разных экземпляра Board, поэтому ваш вызов Assert.AreEqual завершится с ошибкой. Даже если все их содержимое выглядит одинаково, вы сравниваете ссылки, а не базовые значения.

Вы должны указать, что делает два экземпляра Board равными.

Вы можете сделать это в своем тесте:

Assert.AreEqual(expected.Rows.Count, actual.Rows.Count);
Assert.AreEqual(expected.Rows[0].Cells[0], actual.Rows[0].Cells[0]);

// Lots more tests of equality...

Или вы можете сделать это в своих классах: (обратите внимание, что я написал это "на лету" - вы хотите настроить это)

public class Board
{
    public List<Row> Rows = new List<Row>();

    public override bool Equals(object obj)
    {
        var board = obj as Board;

        if (board == null)
            return false;

        if (board.Rows.Count != Rows.Count)
            return false;

        return !board.Rows.Where((t, i) => !t.Equals(Rows[i])).Any();
    }

    public override int GetHashCode()
    {
        // determine what appropriate to return here - a unique board id may be appropriate if available
    }
}

public class Row
{
    public List<int> Cells = new List<int>(); 

    public override bool Equals(object obj)
    {
        var row = obj as Row;

        if (row == null)
            return false;

        if (row.Cells.Count != Cells.Count)
            return false;

        if (row.Cells.Except(Cells).Any())
            return false;

        return true;
    }

    public override int GetHashCode()
    {
        // determine what appropriate to return here - a unique row id may be appropriate if available
    }
}

Ответ 2

Я использовал, чтобы переопределить getHasCode и равный, но мне это никогда не нравилось, так как я не хочу менять свой производственный код для модульного тестирования. Также это боль.

Затем я стал слишком размышлять, чтобы сравнить объекты, которые были менее инвазивны... но такой объем работы (много угловых случаев)

В конце я использую:

http://www.nuget.org/packages/DeepEqual/

Отлично работает.

Ответ 3

ExpectedObjects поможет вам сравнить равенство по значению свойства. Он поддерживает:

  1. простой объект: ожидается. ToExpectedObject(). ShouldEqual (актуальный);
  2. collection: ожидается. ToExpectedObject(). ShouldEqual (фактическое);
  3. составной объект: ожидается. ToExpectedObject(). ShouldEqual (актуальный);
  4. частичное сравнение: ожидаемый объект нуждается в дизайне с анонимным типом и использует ожидается. ToExpectedObject(). ShouldMatch (актуальный)

Я люблю ExpectedObjects, потому что мне нужно всего лишь вызвать 2 API для утверждения сравнения равенства объектов:

  1. ShouldEqual()
  2. ShouldMatch() для частичного сравнения

Ответ 4

Мне нужно решение, которое не требовало добавления зависимости, работало с VS модульными тестами, сравнивало значения полей двух объектов и рассказывало мне все неравные поля. Это то, что я придумал. Обратите внимание, что он может быть расширен и для работы со значениями свойств.

В моем случае это хорошо работает для сравнения результатов некоторой логики синтаксического анализа файлов, чтобы гарантировать, что две технически "разные" записи имеют поля с одинаковыми значениями.

    public class AssertHelper
    {
        public static void HasEqualFieldValues<T>(T expected, T actual)
        {
            var failures = new List<string>();
            var fields = typeof(T).GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
            foreach(var field in fields)
            {
                var v1 = field.GetValue(expected);
                var v2 = field.GetValue(actual);
                if (v1 == null && v2 == null) continue;
                if(!v1.Equals(v2)) failures.Add(string.Format("{0}: Expected:<{1}> Actual:<{2}>", field.Name, v1, v2));
            }
            if (failures.Any())
                Assert.Fail("AssertHelper.HasEqualFieldValues failed. " + Environment.NewLine+ string.Join(Environment.NewLine, failures));
        }
    }

    [TestClass]
    public class AssertHelperTests
    {
        [TestMethod]     
        [ExpectedException(typeof(AssertFailedException))]           
        public void ShouldFailForDifferentClasses()
        {            
            var actual = new NewPaymentEntry() { acct = "1" };
            var expected = new NewPaymentEntry() { acct = "2" };
            AssertHelper.HasEqualFieldValues(expected, actual);
        }
    }

Ответ 5

Методы подтверждения полагаются на объект Equals и GetHashcode. Вы можете реализовать это, но если это равенство объекта не требуется вне модульных тестов, я бы вместо этого рассмотрел возможность сравнения отдельных примитивных типов объекта. Похоже, что объекты достаточно просты и переопределение равных на самом деле не оправдано.

Ответ 6

Для тривиальных объектов, таких как доменные объекты, DTO или сущности, вы можете просто сериализовать оба экземпляра в строку и сравнить эту строку:

var object1Json = JsonConvert.SerializeObject(object1);
var object2Json = JsonConvert.SerializeObject(object2);

Assert.AreEqual(object1Json, object2Json);

Это, конечно, имеет различные предостережения, поэтому оцените, содержит ли JSON ожидаемые значения, которые следует сравнивать для ваших классов.

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