Assert.Equal() 在将 IEnumerable(使用 yield return)与自身进行比较时失败
Assert.Equal() fails when comparing IEnumerable (using yield return) with itself
运行 在为我的代码编写单元测试时遇到以下问题。为什么在将 IEnumerable 与其自身进行比较时 Assert.Equal() 会失败?
private class ReferenceType { }
[Fact]
public void EnumerableEqualityTest()
{
IEnumerable<ReferenceType> GetEnumerable()
{
yield return new ReferenceType();
}
var enumerable = GetEnumerable();
Assert.Equal(enumerable, enumerable); // fails
}
要了解正在发生的事情,我们需要了解 Assert.Equal()
实际在做什么。根据 Assert.Equal<T>(IEnumerable<T> expected, IEnumerable<T> actual)
的文档,它 "Verifies that two sequences are equivalent, using a default comparer".
本例中的 Assert.Equal()
迭代可枚举以检查各个值是否相等。这意味着枚举被迭代两次以进行比较,并且每次都会创建一个 ReferenceType
的新实例(通过 yield return)。测试失败,因为引用类型的默认比较器仅检查实例是否引用同一对象。
至少有三种方法可以获得预期的结果:
- 使用参数为
IEqualityComparer<T>
的 Assert.Equal()
的重载。
- 覆盖
ReferenceType
的 Equals()
方法。
- 跳过
yield return
并使用实现 IEnumerable
的集合。
可以说第一个解决方案是最好的,因为它不会改变 GetEnumerable()
或 ReferenceType
的实现。在这种 GetEnumerable()
仅在测试中使用的情况下,我会选择第三个选项,因为它最容易做到。它可能看起来像这样:
IEnumerable<ReferenceType> GetData()
{
return new[] { new ReferenceType() };
}
或者这个:
IEnumerable<ReferenceType> GetData()
{
var referenceTypes = new List<ReferenceType>();
// ... add reference types
return referenceTypes;
}
这是有效的,因为我们现在正在迭代我们获得可枚举对象时创建的集合,而不是为每次迭代创建新实例。
我认为,您可以使用以下功能来解决问题。
public bool AreEquivalentEnumerator(IEnumerator<TSource> first, IEnumerator<TSource> second)
{
while (first.MoveNext())
{
if (!(second.MoveNext() && Equals(first.Current, second.Current))) return false;
}
if (second.MoveNext()) return false;
return true;
}
运行 在为我的代码编写单元测试时遇到以下问题。为什么在将 IEnumerable 与其自身进行比较时 Assert.Equal() 会失败?
private class ReferenceType { }
[Fact]
public void EnumerableEqualityTest()
{
IEnumerable<ReferenceType> GetEnumerable()
{
yield return new ReferenceType();
}
var enumerable = GetEnumerable();
Assert.Equal(enumerable, enumerable); // fails
}
要了解正在发生的事情,我们需要了解 Assert.Equal()
实际在做什么。根据 Assert.Equal<T>(IEnumerable<T> expected, IEnumerable<T> actual)
的文档,它 "Verifies that two sequences are equivalent, using a default comparer".
本例中的 Assert.Equal()
迭代可枚举以检查各个值是否相等。这意味着枚举被迭代两次以进行比较,并且每次都会创建一个 ReferenceType
的新实例(通过 yield return)。测试失败,因为引用类型的默认比较器仅检查实例是否引用同一对象。
至少有三种方法可以获得预期的结果:
- 使用参数为
IEqualityComparer<T>
的Assert.Equal()
的重载。 - 覆盖
ReferenceType
的Equals()
方法。 - 跳过
yield return
并使用实现IEnumerable
的集合。
可以说第一个解决方案是最好的,因为它不会改变 GetEnumerable()
或 ReferenceType
的实现。在这种 GetEnumerable()
仅在测试中使用的情况下,我会选择第三个选项,因为它最容易做到。它可能看起来像这样:
IEnumerable<ReferenceType> GetData()
{
return new[] { new ReferenceType() };
}
或者这个:
IEnumerable<ReferenceType> GetData()
{
var referenceTypes = new List<ReferenceType>();
// ... add reference types
return referenceTypes;
}
这是有效的,因为我们现在正在迭代我们获得可枚举对象时创建的集合,而不是为每次迭代创建新实例。
我认为,您可以使用以下功能来解决问题。
public bool AreEquivalentEnumerator(IEnumerator<TSource> first, IEnumerator<TSource> second)
{
while (first.MoveNext())
{
if (!(second.MoveNext() && Equals(first.Current, second.Current))) return false;
}
if (second.MoveNext()) return false;
return true;
}