断言两个不同的对象是否在动态集合中具有相同的值
Assert if two different objects have same values in a dynamic collection
在比较动态集合(ViewBag)中的两个对象时,我 运行 遇到了 xUnit.net 的一个相当奇怪的问题。
我有一个使用以下方法的 ActionFilter:
public void OnActionExecuting(ActionExecutingContext filterContext)
{
var selectList = new List<SelectListItem>();
var foos = _repo.Get();
foreach (var foo in foos)
{
var selectItem = new SelectListItem()
{
Text = foo.Text,
Value = foo.Value
};
selectList.Add(selectItem);
}
filterContext.Controller.ViewBag.SelectList = selectList;
}
请注意这些值是如何包装在 List<SelectListItem>
中,然后分配给 ViewBag
。
然后我有一个测试,测试我的存储库中的值是否被添加到 ViewBag:
public void MyTest()
{
// Arrange
var fakeController = Substitute.For<Controller>();
var fakeContext = Substitute.For<ActionExecutingContext>();
fakeContext.Controller = fakeController;
var repository = Substitute.For<IRepository<Foo>>();
var foo = new Foo() {Text = "Foo", Value = "Bar"};
var foos = new List<Foo> { foo };
repository.Get().Returns(foos);
var filter = new MyFilter(repository);
// Act
filter.OnActionExecuting(fakeContext);
// Assert
var expected = new List<SelectListItem> {new SelectListItem {Text = foo.Text, Value = foo.Value}};
Assert.Equal(expected, fakeContext.Controller.ViewBag.SelectList); // fails
}
此测试失败
Result Message:
Assert.Equal() Failure
Expected: List [SelectListItem { Disabled = False, Group = null, Selected = False, Text = "Foo", Value = "Bar" }]
Actual: List [SelectListItem { Disabled = False, Group = null, Selected = False, Text = "Foo", Value = "Bar" }]
对我来说这看起来是一样的。
以防万一我测试它是否意外地检查它是否是同一个实例。但是下面通过了。所以不是这样的。
var a = new {a = "a"};
var b = new {a = "a"};
Assert.Equal(a, b); // pass
Assert.Equal 将调用对象的 "Equals" 方法。对于 List,它只是继承自 Object,对于引用类型,它测试实例是否相等(即相同的实例)。
相反,尝试 Enumerable.SequenceEqual(参见 here)
已更新以包含 EqualityComparer 的实现:
// Custom comparer for the SelectListItem class
class SelectListItemComparer : IEqualityComparer<SelectListItem>
{
// Products are equal if their names and product numbers are equal.
public bool Equals(SelectListItem x, SelectListItem y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
//Check whether the products' properties are equal.
return x.Text.Equals(y.Text) && x.Value.Equals(y.Value);
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(SelectListItem item)
{
//Check whether the object is null
if (Object.ReferenceEquals(item, null)) return 0;
//Get hash code for the Name field if it is not null.
int hashText = item.Text == null ? 0 : item.Text.GetHashCode();
//Get hash code for the Code field.
int hashValue = item.Value.GetHashCode();
//Calculate the hash code for the product.
return hashText ^ hashValue;
}
}
那么我们可以这样做:
// Assert
var expected = new List<SelectListItem> {new SelectListItem {Text = "this", Value = "that"}};
var actual = new List<SelectListItem> {new SelectListItem {Text = "this", Value = "that"}};
Assert.IsTrue(expected.SequenceEqual(actual, new SelectListItemComparer()));
在比较动态集合(ViewBag)中的两个对象时,我 运行 遇到了 xUnit.net 的一个相当奇怪的问题。
我有一个使用以下方法的 ActionFilter:
public void OnActionExecuting(ActionExecutingContext filterContext)
{
var selectList = new List<SelectListItem>();
var foos = _repo.Get();
foreach (var foo in foos)
{
var selectItem = new SelectListItem()
{
Text = foo.Text,
Value = foo.Value
};
selectList.Add(selectItem);
}
filterContext.Controller.ViewBag.SelectList = selectList;
}
请注意这些值是如何包装在 List<SelectListItem>
中,然后分配给 ViewBag
。
然后我有一个测试,测试我的存储库中的值是否被添加到 ViewBag:
public void MyTest()
{
// Arrange
var fakeController = Substitute.For<Controller>();
var fakeContext = Substitute.For<ActionExecutingContext>();
fakeContext.Controller = fakeController;
var repository = Substitute.For<IRepository<Foo>>();
var foo = new Foo() {Text = "Foo", Value = "Bar"};
var foos = new List<Foo> { foo };
repository.Get().Returns(foos);
var filter = new MyFilter(repository);
// Act
filter.OnActionExecuting(fakeContext);
// Assert
var expected = new List<SelectListItem> {new SelectListItem {Text = foo.Text, Value = foo.Value}};
Assert.Equal(expected, fakeContext.Controller.ViewBag.SelectList); // fails
}
此测试失败
Result Message:
Assert.Equal() FailureExpected: List [SelectListItem { Disabled = False, Group = null, Selected = False, Text = "Foo", Value = "Bar" }]
Actual: List [SelectListItem { Disabled = False, Group = null, Selected = False, Text = "Foo", Value = "Bar" }]
对我来说这看起来是一样的。
以防万一我测试它是否意外地检查它是否是同一个实例。但是下面通过了。所以不是这样的。
var a = new {a = "a"};
var b = new {a = "a"};
Assert.Equal(a, b); // pass
Assert.Equal 将调用对象的 "Equals" 方法。对于 List,它只是继承自 Object,对于引用类型,它测试实例是否相等(即相同的实例)。
相反,尝试 Enumerable.SequenceEqual(参见 here)
已更新以包含 EqualityComparer 的实现:
// Custom comparer for the SelectListItem class
class SelectListItemComparer : IEqualityComparer<SelectListItem>
{
// Products are equal if their names and product numbers are equal.
public bool Equals(SelectListItem x, SelectListItem y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
//Check whether the products' properties are equal.
return x.Text.Equals(y.Text) && x.Value.Equals(y.Value);
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(SelectListItem item)
{
//Check whether the object is null
if (Object.ReferenceEquals(item, null)) return 0;
//Get hash code for the Name field if it is not null.
int hashText = item.Text == null ? 0 : item.Text.GetHashCode();
//Get hash code for the Code field.
int hashValue = item.Value.GetHashCode();
//Calculate the hash code for the product.
return hashText ^ hashValue;
}
}
那么我们可以这样做:
// Assert
var expected = new List<SelectListItem> {new SelectListItem {Text = "this", Value = "that"}};
var actual = new List<SelectListItem> {new SelectListItem {Text = "this", Value = "that"}};
Assert.IsTrue(expected.SequenceEqual(actual, new SelectListItemComparer()));