.net C# -在单元测试中Assert两个对象相等

jckbn6z7  于 12个月前  发布在  .NET
关注(0)|答案(9)|浏览(143)

使用Nunit或Microsoft.VisualStudio.TestTools.UnitTesting。现在我的Assert失败了。

[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);
    }
nszi6y05

nszi6y051#

您有两个不同的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
    }
}
von4xj4u

von4xj4u2#

我曾经覆盖getHasCode和equals,但我从来不喜欢它,因为我不想为了单元测试而改变我的生产代码。也是一种痛苦。
然后我把太多的反射,比较对象,这是侵入性较低......但这是一种大量的工作(许多角落的情况下)
最后我用途:
http://www.nuget.org/packages/DeepEqual/工作得很好。

更新,6年后

我现在使用更通用的.NET库fluentassertions,它与上面的功能相同,但具有更多的功能和一个很好的DSL,具体的替代方案是:https://fluentassertions.com/objectgraphs/

PM> Install-Package FluentAssertions

经过几年的经验,我仍然不建议覆盖路线,我甚至认为这是一个坏的做法。如果你不小心,你可能会在使用一些集合(如字典)时引入性能问题。另外,当你有一个真实的业务案例来重载这些方法的时候,你会有麻烦,因为你已经有了这个测试代码。生产代码和测试代码应该保持分离,测试代码不应该依赖于实现细节或黑客来实现它们的目标,这使得它们难以维护和理解。

anauzrmj

anauzrmj3#

对于普通对象,比如域对象、DTO或实体,你可以简单地将两个示例序列化为一个字符串,然后比较该字符串:

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

Assert.AreEqual(object1Json, object2Json);

当然,这有各种警告,所以评估一下对于你的类,JSON是否包含应该比较的预期值。
例如,如果类包含非托管资源或不可序列化的属性,则无法正确比较这些资源或属性。默认情况下,它也只序列化公共属性。

js5cn81o

js5cn81o4#

ExpectedObjects将帮助您通过属性值比较相等性。它支持:
1.简单对象:return.ToExpectedObject().ShouldEqual(actual);
1.收藏:return.ToExpectedObject().ShouldEqual(actual);
1.合成对象:return.ToExpectedObject().ShouldEqual(actual);
1.部分比较:预期对象需要设计为匿名类型,并使用expected.ToExpectedObject().ShouldMatch(actual)
我喜欢ExpectedObjects,因为我只需要调用2个API来Assert比较对象的相等性:

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

jobtbby35#

由于在这个问题上还没有提到,有几个其他被广泛采用的库可以帮助解决这个问题:

  1. Fluent Assertions-参见object graph comparison
    actual.Should().BeEquivalentTo(expected);
  2. Semantic Comparison
    Likeness<MyModel, MyModel>(actual).ShouldEqual(expected);
    我个人更喜欢Fluent Assertions,因为它在成员排除等方面提供了更大的灵活性,并且它支持开箱即用的嵌套对象的比较。
    希望这对你有帮助!
iezvtpos

iezvtpos6#

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

jei2mxaa7#

Assert方法依赖于对象的Equals和GetHashcode。你可以实现它,但是如果在单元测试之外不需要这个对象相等,我会考虑比较对象上的各个原始类型。看起来对象足够简单,并且不需要覆盖equals。

62o28rlo

62o28rlo8#

如果只想比较复杂类型对象的属性,迭代对象属性将给出所有属性。你可以试试下面的代码。

//Assert
foreach(PropertyInfo property in Object1.GetType().GetProperties())
{
    Assert.AreEqual(property.GetValue(Object1), property.GetValue(Object2));
}
hmmo2u0o

hmmo2u0o9#

也可以使用IEqualityComparer。您可以在顶部添加可比较的方法,而不必重写类本身。如果您想要更深入的检查,可以查看如何处理GetHashCode

Assert.AreEqual(expected.Rows.Count, actual.Rows.Count, new BoardEqualityComparerMethod());

public class BoardEqualityComparerMethod : IEqualityComparer<Board>
{
    public bool Equals(Board x, Board y)
    {
        if (x == null)
            return y == null;
        else if (y == null)
            return false;
        else
            //Do a check here using Foreach or creating it's own IEqualityComparer
            return x.Rows == y.Rows;
    }

    public int GetHashCode(Board obj)
    {
        //do hashing if needed
        return obj.GetHashCode();
    }
}

相关问题