C# - 在单元测试中断言两个对象是相同的

时间:2014-05-08 01:00:01

标签: c# .net unit-testing tdd nunit

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

7 个答案:

答案 0 :(得分:16)

您有两个不同的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's 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's appropriate to return here - a unique row id may be appropriate if available
    }
}

答案 1 :(得分:10)

我曾经覆盖了getHasCode和equals,但我从不喜欢它,因为我不想为了单元测试而改变我的生产代码。 这也是一种痛苦。

然后我过于反思来比较那些侵入性较小的物体......但那是很多工作(很多角落的情况)

最后我使用:

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

效果很好。

答案 2 :(得分:7)

ExpectedObjects可以帮助您按属性值比较相等性。它支持:

  1. simple object:expected.ToExpectedObject()。ShouldEqual(actual);
  2. collection:expected.ToExpectedObject()。ShouldEqual(actual);
  3. composized object:expected.ToExpectedObject()。ShouldEqual(actual);
  4. 部分比较:预期对象需要设计匿名类型,并使用expected.ToExpectedObject()。ShouldMatch(actual)
  5. 我喜欢ExpectedObjects,因为我只需要调用2个API来断言比较对象的相等性:

    1. ShouldEqual()
    2. 用于部分比较的ShouldMatch()

答案 3 :(得分:7)

我想要一个不需要添加依赖项的解决方案,使用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);
        }
    }

答案 4 :(得分:2)

对于琐碎的对象(例如域对象或DTO或实体),您只需将两个实例序列化为一个字符串并比较该字符串:

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

Assert.AreEqual(object1Json, object2Json);

这当然有很多注意事项,因此请评估JSON是否包含您应该比较的期望值。

例如,如果您的类包含非托管资源或其他不可序列化的属性,则将无法正确比较这些属性。默认情况下,它也只序列化公共属性。

答案 5 :(得分:1)

由于尚未在此问题上提到任何人,因此还有一些其他采用良好的库可以帮助解决此问题:

  1. Fluent Assertions-参见object graph comparison

    actual.Should().BeEquivalentTo(expected);

  2. Semantic Comparison

    Likeness<MyModel, MyModel>(actual).ShouldEqual(expected);

我个人更喜欢Fluent断言,因为它在成员排除等方面提供了更大的灵活性,并且支持开箱即用地比较嵌套对象。

希望这会有所帮助!

答案 6 :(得分:0)

Assert方法依赖于对象的Equals和GetHashcode。你可以实现它,但如果在单元测试之外不需要这个对象相等,我会考虑比较对象上的各个基本类型。 看起来这些对象很简单,并且不能保证等于等等。