我在单元测试中有两个对象,一个是实际对象,另一个是期望对象。对象方法上的所有属性都完全相同,如果我运行以下测试:
Assert.AreEqual( expectedObject.Property1, actualObject.Property1);
测试结果符合预期。但是,当我尝试运行以下测试时,它失败了:
Assert.AreEqual (expectedObject, actualObject);
我错在哪里?两个对象不能进行比较吗?我是否需要对每个属性都进行检查?
我在单元测试中有两个对象,一个是实际对象,另一个是期望对象。对象方法上的所有属性都完全相同,如果我运行以下测试:
Assert.AreEqual( expectedObject.Property1, actualObject.Property1);
测试结果符合预期。但是,当我尝试运行以下测试时,它失败了:
Assert.AreEqual (expectedObject, actualObject);
我错在哪里?两个对象不能进行比较吗?我是否需要对每个属性都进行检查?
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
除非你已经将它重载到你的对象里面,否则不能使用'= '符号。在你的对象类中,你需要做一些像这样的事情:
public override bool Equals(object obj)
{
if(obj == null)
return false;
return (this.Property1 == obj.Property1 &&
this.Property2 == obj.Property2);
}
如果你不这样做,那么你只是简单地比较对象引用。
这是真的。两个对象是否相等完全取决于它们Equals方法的实现。有时候,使用Equal方法,重写GetHashCode实现也是很好的选择。如果没有重写,将会考虑默认的虚拟实现(Object类)以及Equal方法的评估。为了使对象被视为相等,它们的哈希码应该相同。
然而,在单元测试中,我建议不要过分依赖Equal方法的实现。无论你想确定哪些属性,都应该自己比较对象的这些属性,因为Equal方法可能是自定义实现,所以最终可能会包含错误。因此,在单元测试中最好信任系统定义的类的Equal方法。
这是一个典型的“等价性”问题,看起来被接受的答案并不好。我会尝试解释一下为什么。
想象一下以下情况 - 你需要在后端编写集成测试以确保它正确地存储了你的领域对象。 你有如下代码:
[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);
https://github.com/kbilsted/StatePrinter是专门用于将对象图转储为字符串表示形式的编写,旨在编写易于单元测试的工具。
鉴于此
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);
Deepak: 这意味着最终我不会比较对象,而是比较对象属性值。对吗?这个等于重写方法只适用于特定的对象...这是一种限制,而不是正确的方式...
这里有一个好链接...在准备单元测试之前,为自己的类编写测试帮助。
http://manfred-ramoser.blogspot.com/2007/11/c-unit-testing-helper.html
我知道这是一个老问题,但当我阅读答案时,我想到了一个简单的技巧。也许其他人已经知道这个技巧,但因为它没有在这里提到...
我已经将Newtonsoft.Json作为我的项目的一部分。所以我可以轻松地在预期和实际上使用JsonConvert.SerializeObject(obj)。然后只需要对两个字符串进行Assert.AreEqual。
对我有用 :)