断言 2 个对象中的所有字段都相同 C#
assert that all fields in 2 objects are the same c#
我在做单元测试,主要是想检查2个对象持有的数据是否相同
Assert.AreEqual(object1, object2);
Assert.IsTrue(object1.Equals(object2)); //this of course doesn't work
我正在搜索 assertJ
的 C# 等价物
Assert.That(object1).isEqualToComparingFieldByField(object2)
您可以使用记录 (c# 9 +),或者您必须重写 Equals 方法(如果您有权访问并且可以更改您正在使用的对象)。
记录示例:
var point = new Point(3, 4);
var point2 = new Point(3, 4);
var test = point.Equals(point2); //this is true
public record Point(int X, int Y);
与 类:
public class Point
{
public int X { get; }
public int Y { get; }
public override bool Equals(object? obj)
{
if (obj == null)
return false;
return obj is Point point && (point.X == X && point.Y == Y);
}
public override int GetHashCode()
{
return HashCode.Combine(X, Y);
}
}
如果不允许触及实现,则可以使用序列化并比较字符串:
var obj1Str = JsonConvert.SerializeObject(object1);
var obj2Str = JsonConvert.SerializeObject(object2);
Assert.Equal(obj1Str, obj2Str);
使用 Newtonsoft.Json nuget
C# classes 是引用相等性,这意味着使用标准 Equals
和 ==
的变量是相同的,如果它们指向同一个对象,您可以覆盖该行为, 但它现在或将来可能会破坏某些东西。
或者,您可以切换到使用默认值相等的构造,struct
和 record class
都是。如果你不能(或不想)这样做,你可以自己实现一个值等于“helper”的方法。我 不 建议重写 Equals
方法或 ==
运算符,因为这可能(而且很可能会)在将来导致错误,而不是我建议你编写您自己的 ValueEquals
方法或扩展方法,类似于
class Foo
{
public int Count {get; set;}
public string Message {get; set;}
}
public static bool ValueEquals(this Foo self, Foo other)
{
return self.Count == other.Count && self.Message == other.Message;
}
public void MyTest()
{
// Arrange and Act
...
// Assert
Assert.IsTrue(myFoo1.ValueEquals(myFoo2));
}
根据您是否可以/想要添加 ValueEquals
到您的 Foo
class,您可以决定使用扩展方法还是普通方法。
您还可以实施 IEqualityComparer<T>
之类的
public class FooValueEqualityComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo foo1, Foo foo2)
{
return foo1.Count == foo2.Count &&
foo1.Message == foo2.Message;
}
public int GetHashCode(Foo foo)
{
return foo.GetHashCode();
}
}
// Use it
public void MyTest()
{
// Arrange and Act
...
// Assert
Assert.IsTrue(new FooEqualityComparer().Equals(myFoo1, myFoo2));
}
或,您可以使用反射编写适用于所有^* class的通用ValueEquals
:
public static class ValueEqualityComparer
{
public static bool ValueEquals<T>(this T self, T other) where T : class
{
var type = self.GetType();
if (type == typeof(string))
return self.Equals(other);
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
var selfValue = property.GetValue(self);
var otherValue = property.GetValue(other);
// String is special, it's not primitive but is value equality like primitives
if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(string))
{
if (!selfValue.Equals(otherValue))
return false;
}
// If the property is a List value equals each member
// Maybe find another type that allows indexing and is less restrictive
else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
var selfList = ((IEnumerable)property.GetValue(self)).Cast<object>();
var otherList = ((IEnumerable)property.GetValue(other)).Cast<object>();
try
{
// Using EquiZip from MoreLinq: https://github.com/morelinq/MoreLINQ/blob/master/MoreLinq/EquiZip.cs
foreach (var element in selfList.EquiZip(otherList, (selfItem, otherItem) => new { selfItem, otherItem }))
{
if (!ValueEquals(element.selfItem, element.otherItem))
return false;
}
}
catch (InvalidOperationException)
{
// MoreLINQ throws a InvalidOperationException if our two enumerables aren't the same length
return false;
}
}
else
{
if (!ValueEquals(selfValue, otherValue))
return false;
}
}
return true;
}
}
这个实现绝不是完美的,老实说,应该只用于单元测试,也应该彻底测试自己。您可以将我的测试视为 dotnetfiddle here
或者您可以将其“弄脏”并将对象序列化为字符串并比较这些值。
我在做单元测试,主要是想检查2个对象持有的数据是否相同
Assert.AreEqual(object1, object2);
Assert.IsTrue(object1.Equals(object2)); //this of course doesn't work
我正在搜索 assertJ
的 C# 等价物Assert.That(object1).isEqualToComparingFieldByField(object2)
您可以使用记录 (c# 9 +),或者您必须重写 Equals 方法(如果您有权访问并且可以更改您正在使用的对象)。 记录示例:
var point = new Point(3, 4);
var point2 = new Point(3, 4);
var test = point.Equals(point2); //this is true
public record Point(int X, int Y);
与 类:
public class Point
{
public int X { get; }
public int Y { get; }
public override bool Equals(object? obj)
{
if (obj == null)
return false;
return obj is Point point && (point.X == X && point.Y == Y);
}
public override int GetHashCode()
{
return HashCode.Combine(X, Y);
}
}
如果不允许触及实现,则可以使用序列化并比较字符串:
var obj1Str = JsonConvert.SerializeObject(object1);
var obj2Str = JsonConvert.SerializeObject(object2);
Assert.Equal(obj1Str, obj2Str);
使用 Newtonsoft.Json nuget
C# classes 是引用相等性,这意味着使用标准 Equals
和 ==
的变量是相同的,如果它们指向同一个对象,您可以覆盖该行为, 但它现在或将来可能会破坏某些东西。
或者,您可以切换到使用默认值相等的构造,struct
和 record class
都是。如果你不能(或不想)这样做,你可以自己实现一个值等于“helper”的方法。我 不 建议重写 Equals
方法或 ==
运算符,因为这可能(而且很可能会)在将来导致错误,而不是我建议你编写您自己的 ValueEquals
方法或扩展方法,类似于
class Foo
{
public int Count {get; set;}
public string Message {get; set;}
}
public static bool ValueEquals(this Foo self, Foo other)
{
return self.Count == other.Count && self.Message == other.Message;
}
public void MyTest()
{
// Arrange and Act
...
// Assert
Assert.IsTrue(myFoo1.ValueEquals(myFoo2));
}
根据您是否可以/想要添加 ValueEquals
到您的 Foo
class,您可以决定使用扩展方法还是普通方法。
您还可以实施 IEqualityComparer<T>
之类的
public class FooValueEqualityComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo foo1, Foo foo2)
{
return foo1.Count == foo2.Count &&
foo1.Message == foo2.Message;
}
public int GetHashCode(Foo foo)
{
return foo.GetHashCode();
}
}
// Use it
public void MyTest()
{
// Arrange and Act
...
// Assert
Assert.IsTrue(new FooEqualityComparer().Equals(myFoo1, myFoo2));
}
或,您可以使用反射编写适用于所有^* class的通用ValueEquals
:
public static class ValueEqualityComparer
{
public static bool ValueEquals<T>(this T self, T other) where T : class
{
var type = self.GetType();
if (type == typeof(string))
return self.Equals(other);
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
var selfValue = property.GetValue(self);
var otherValue = property.GetValue(other);
// String is special, it's not primitive but is value equality like primitives
if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(string))
{
if (!selfValue.Equals(otherValue))
return false;
}
// If the property is a List value equals each member
// Maybe find another type that allows indexing and is less restrictive
else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
var selfList = ((IEnumerable)property.GetValue(self)).Cast<object>();
var otherList = ((IEnumerable)property.GetValue(other)).Cast<object>();
try
{
// Using EquiZip from MoreLinq: https://github.com/morelinq/MoreLINQ/blob/master/MoreLinq/EquiZip.cs
foreach (var element in selfList.EquiZip(otherList, (selfItem, otherItem) => new { selfItem, otherItem }))
{
if (!ValueEquals(element.selfItem, element.otherItem))
return false;
}
}
catch (InvalidOperationException)
{
// MoreLINQ throws a InvalidOperationException if our two enumerables aren't the same length
return false;
}
}
else
{
if (!ValueEquals(selfValue, otherValue))
return false;
}
}
return true;
}
}
这个实现绝不是完美的,老实说,应该只用于单元测试,也应该彻底测试自己。您可以将我的测试视为 dotnetfiddle here
或者您可以将其“弄脏”并将对象序列化为字符串并比较这些值。