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

Per*_*ddy 16 .net c# tdd nunit unit-testing

使用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);
    }
Run Code Online (Sandbox Code Playgroud)

Gra*_*ICA 18

您有两个不同的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...
Run Code Online (Sandbox Code Playgroud)

或者你可以在课堂上做到这一点:( 注意我是在飞行中写的 - 你要调整它)

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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为值得注意的是这种方法的一些缺点:它将单元测试代码粘贴在您的生产代码中,它创建了更多本身必须进行单元测试的生产代码,该代码可能会改变其他生产代码的行为,但实际上并没有减少您需要的代码总量。 (2认同)

foo*_*ode 10

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

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

最后我使用:

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

效果很好.

  • 是的,您可以执行如下断言:“actual.ShouldDeepEqual(expected);”,其中实际和预期是要比较的两个对象。 (3认同)

Sim*_*ess 8

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

  1. 流畅的断言- 请参阅对象图比较

    actual.Should().BeEquivalentTo(expected);

  2. 语义比较

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

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

希望这可以帮助!


小智 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)

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

  1. ShouldEqual()
  2. ShouldMatch()进行部分比较


Dan*_*iel 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);
        }
    }
Run Code Online (Sandbox Code Playgroud)


Cod*_*ter 6

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

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

Assert.AreEqual(object1Json, object2Json);
Run Code Online (Sandbox Code Playgroud)

这当然有各种警告,因此请评估您的类,JSON 是否包含应该比较的预期值。

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