单元测试中的对象比较

15

我在单元测试中有两个对象,一个是实际对象,另一个是期望对象。对象方法上的所有属性都完全相同,如果我运行以下测试:

Assert.AreEqual( expectedObject.Property1, actualObject.Property1);

测试结果符合预期。但是,当我尝试运行以下测试时,它失败了:

Assert.AreEqual (expectedObject, actualObject);

我错在哪里?两个对象不能进行比较吗?我是否需要对每个属性都进行检查?


2
这里有一个类似的关于 NUnit 的问题,可能会有所帮助:https://dev59.com/pHRC5IYBdhLWcg3wXP0C - Chris Van Opstal
为了实际执行你想要的工作,我编写了一个实用类,通过使用反射来比较两个对象的属性。目前我手头没有代码,但是实现这样的功能并不困难。 - Juri
如果你遇到了那段代码Juri,我很想看看你做了什么。 - Blake Blackwell
8个回答

17
您需要为您的对象重写Equals方法。Assert使用Object.Equals。默认情况下,引用类型对象上的Object.Equals执行引用比较。也就是说,只有当它们引用同一个对象时,两个引用类型的实例才相等。您希望覆盖此行为,以便执行值比较而不是引用比较。这里有一篇非常好的MSDN文章介绍了这个主题。请注意,您还需要重写GetHashCode。请参阅MSDN的指南。以下是一个简单的示例:

修改后:

class Test {
    public int Value { get; set; }
}

Test first = new Test { Value = 17 };
Test second = new Test { Value = 17 };
Console.WriteLine(first.Equals(second)); // false

之后:

class Test {
    public int Value { get; set; }
    public override bool Equals(object obj) {
        Test other = obj as Test;
        if(other == null) {
            return false; 
        }
        return this.Value == other.Value;
    }
    public override int GetHashCode() { 
        return this.Value.GetHashCode();
    }
}

Test first = new Test { Value = 17 };
Test second = new Test { Value = 17 };
Console.WriteLine(first.Equals(second)); // true

3
注意避免“等值污染”问题。重写 Equals 可能并不正确。请参考此处获取更多信息:https://dev59.com/LHI-5IYBdhLWcg3wBjrl#2047576 - Mark Seemann

4
第二个assert语句实际上比较的是对象的引用,而不是内容。由于AreEqual方法的参数是对象类型,因此单元测试框架在如何比较这些对象方面没有太多信息。
编辑:请查看此问题:在NUnit中比较两个对象的相等性

1
啊,太晚了 :) 刚想写同样的东西 :) (+1) - Juri
啊,好的。我发帖时没有看到nUnit的问题。 - Blake Blackwell

1

除非你已经将它重载到你的对象里面,否则不能使用'= '符号。在你的对象类中,你需要做一些像这样的事情:


public override bool Equals(object obj)
{
   if(obj == null)
      return false;
   return (this.Property1 == obj.Property1 && 
          this.Property2 == obj.Property2);
}

如果你不这样做,那么你只是简单地比较对象引用。


1

这是真的。两个对象是否相等完全取决于它们Equals方法的实现。有时候,使用Equal方法,重写GetHashCode实现也是很好的选择。如果没有重写,将会考虑默认的虚拟实现(Object类)以及Equal方法的评估。为了使对象被视为相等,它们的哈希码应该相同。

然而,在单元测试中,我建议不要过分依赖Equal方法的实现。无论你想确定哪些属性,都应该自己比较对象的这些属性,因为Equal方法可能是自定义实现,所以最终可能会包含错误。因此,在单元测试中最好信任系统定义的类的Equal方法。


0

这是一个典型的“等价性”问题,看起来被接受的答案并不好。我会尝试解释一下为什么。

想象一下以下情况 - 你需要在后端编写集成测试以确保它正确地存储了你的领域对象。 你有如下代码:

[TestMethod]
    [Description(@"Sequentially perform operations
                 1. Save new item in DB
                 2. Get same Item from DB
                 Ensure that saved and get Items are equivalent")]
    public void Repository_Create_Test()
    {
        var initialItem = GetTestItem();
        //create item and check it is created correct
        initialItem.ID = repository.Create(initialItem, userID, ownerID);
        Item resultItem = repository.GetById(initialItem.ID, ownerID);
        resultItem.Should().NotBeNull();
        Assert.AreEqual(initialItem, resultItem);
    }

所以,您需要验证从存储中读取的对象与我们发送到存储的对象是绝对等同的。覆盖Equals是一个简单的第一猜测。对于这种情况,我们需要设置Equals来比较所有对象字段。但从DDD的角度来看,这是明显错误的。领域实体通过不可变键(或主键)区分,而不是通过所有可变字段。因此,如果我们对HR领域进行建模,并且假设“ X先生”的新电话号码,则他仍然是同一个“ X先生”。

所有这些都说了,我目前正在使用 FluentAssertions框架,它具有相当强大的等价性检查功能。像这样:

resultItem.ShouldBeEquivalentTo(initialItem);

0

https://github.com/kbilsted/StatePrinter是专门用于将对象图转储为字符串表示形式的编写,旨在编写易于单元测试的工具。

  • 它配备了Assert方法,输出适当转义的字符串,易于复制粘贴到测试中以进行更正。
  • 它允许自动重写unittest
  • 它与所有单元测试框架集成
  • 与JSON序列化不同,支持循环引用
  • 您可以轻松过滤,只转储类型的部分内容

鉴于此

class A
{
  public DateTime X;
  public DateTime Y { get; set; }
  public string Name;
}

您可以以类型安全的方式,并使用 Visual Studio 的自动完成功能包含或排除字段。

  var printer = new Stateprinter();
  printer.Configuration.Projectionharvester().Exclude<A>(x => x.X, x => x.Y);

  var sut = new A { X = DateTime.Now, Name = "Charly" };

  var expected = @"new A(){ Name = ""Charly""}";
  printer.Assert.PrintIsSame(expected, sut);

0

Deepak: 这意味着最终我不会比较对象,而是比较对象属性值。对吗?这个等于重写方法只适用于特定的对象...这是一种限制,而不是正确的方式...

这里有一个好链接...在准备单元测试之前,为自己的类编写测试帮助。

http://manfred-ramoser.blogspot.com/2007/11/c-unit-testing-helper.html


0

我知道这是一个老问题,但当我阅读答案时,我想到了一个简单的技巧。也许其他人已经知道这个技巧,但因为它没有在这里提到...

我已经将Newtonsoft.Json作为我的项目的一部分。所以我可以轻松地在预期和实际上使用JsonConvert.SerializeObject(obj)。然后只需要对两个字符串进行Assert.AreEqual。

对我有用 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接