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);
}
您有两个不同的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);
- 集合:expected.ToExpectedObject () .ShouldEqual(实际);
- 复合对象:expected.ToExpectedObject(). shoulequal (actual);
- 部分比较:期望对象需要匿名类型设计,并使用expected. toexpectedobject ().ShouldMatch(actual)
我喜欢ExpectedObjects,因为我只需要调用2个API来比较对象相等的断言:
- ShouldEqual ()
- 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);
}
}
由于在这个问题上还没有提到这两个库,所以有几个其他被广泛采用的库可以帮助解决这个问题:
-
Fluent断言-参见对象图比较
actual.Should().BeEquivalentTo(expected);
语义比较 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));
}