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

本文关键字:对象 两个 单元测试 中断 断言 | 更新日期: 2023-09-27 18:02:29

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

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

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

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

然后我把太反射来比较那些侵入性较小的物体…但这是一种大量的工作(大量的极端情况)

最后我使用:

http://www.nuget.org/packages/DeepEqual/伟大的工作。

更新,6年后:

我现在使用更通用的。net库fluentassertions它的功能与上面的相同,但有更多的功能和一个不错的DSL,具体的替换将是:https://fluentassertions.com/objectgraphs/

PM> Install-Package FluentAssertions

同样,经过几年的经验,我仍然不推荐重写路线,我甚至认为这是一个坏的做法。如果不小心,在使用某些集合(如dictionary)时可能会引入性能问题。当你有一个真正的商业案例需要重载这些方法的时候,你会遇到麻烦,因为你已经有了测试代码。生产代码和测试代码应该分开,测试代码不应该依赖于实现细节或hack来实现它们的目标,这使得它们很难维护和理解。

对于平凡的对象,如域对象或dto或实体,您可以简单地将两个实例序列化为字符串并比较该字符串:

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

这当然有各种各样的警告,所以评估是否为您的类,JSON包含应该比较的期望值。

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

ExpectedObjects将帮助您通过属性值比较相等性。它支持:

    简单对象:expected.ToExpectedObject().ShouldEqual(actual);
  1. 集合:expected.ToExpectedObject () .ShouldEqual(实际);
  2. 复合对象:expected.ToExpectedObject(). shoulequal (actual);
  3. 部分比较:期望对象需要匿名类型设计,并使用expected. toexpectedobject ().ShouldMatch(actual)

我喜欢ExpectedObjects,因为我只需要调用2个API来比较对象相等的断言:

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

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

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

  1. Fluent断言-参见对象图比较

    actual.Should().BeEquivalentTo(expected);

  2. 语义比较
  3. Likeness<MyModel, MyModel>(actual).ShouldEqual(expected);

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

希望这对你有帮助!

Assert方法依赖于对象的Equals和GetHashcode。您可以实现它,但是如果在单元测试之外不需要这个对象相等,我会考虑比较对象上的各个基本类型。看起来对象足够简单,不需要重写等号。

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

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