如何测试LINQ Range运算符被推迟



我已经实现了某种LINQ Range运算符,并且确实希望进行一个测试,以验证Range运算符是否实际被延迟。

My Range操作员方法:

/// <summary>
/// The Range static method, validation part.
/// </summary>
/// <param name="start">The start.</param>
/// <param name="count">The count.</param>
/// <returns></returns>
public static IEnumerable<int> Range(int start, int count)
{
long max = ((long) start) + count - 1;
if (count < 0 || max > Int32.MaxValue) throw new ArgumentOutOfRangeException(nameof(count));
return RangeIterator(start, count);
}
/// <summary>
/// The Range operator iterator.
/// </summary>
/// <param name="start">The start.</param>
/// <param name="count">The count.</param>
/// <returns></returns>
static IEnumerable<int> RangeIterator(int start, int count)
{
for (int i = 0; i < count; ++i)
{
yield return start + i;
}
}

对于其他延迟运算符,我创建了ThrowingExceptionEnumerable实用程序类,它有助于测试:

/// <summary>
/// The class responsible for verifying that linq operator is deferred.
/// </summary>
/// <typeparam name="T"></typeparam>
public sealed class ThrowingExceptionEnumerable<T> : IEnumerable<T>
{
/// <summary>
/// The methods throws <see cref="InvalidOperationException"/>.
/// </summary>
/// <returns></returns>
public IEnumerator<T> GetEnumerator()
{
throw new InvalidOperationException();
}
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// The method which checks that the given <see cref="deferredFunction"/> actually uses deferred execution.
/// When the function just call itself it should not throw an exception. But, when using the result
/// by calling <see cref="GetEnumerator"/> and than GetNext() methods should throws the <see cref="InvalidOperationException"/>.
/// </summary>
/// <typeparam name="TSource">The deferred function source type.</typeparam>
/// <typeparam name="TResult">The deferred function result type.</typeparam>
/// <param name="deferredFunction">The deferred function (unit of work under the test).</param>
public static void AssertDeferred<TSource,TResult>(
Func<IEnumerable<TSource>, IEnumerable<TResult>> deferredFunction)
{
var source = new ThrowingExceptionEnumerable<TSource>();

// Does not throw any exception here, because GetEnumerator() method is not yet used.
var result = deferredFunction(source);
// Does not throw InvalidOperationException even here, despite the fact that we retrieve the enumerator.
using var iterator = result.GetEnumerator();
Assert.Throws<InvalidOperationException>(() => iterator.MoveNext());
}

例如,延迟选择操作员有以下测试:

/// <summary>
/// Should check that Select operator is deferred.
/// </summary>
[Fact]
public void VerifySelectExecutionIsDeferred()
{
ThrowingExceptionEnumerable<int>.AssertDeferred<int, int>(source => source.Select(x => x));
}

在为Range运算符编写这样的单元测试时,我遇到的第一个问题是Range实际上是一个静态方法,而不是一个扩展方法。此外,Range签名没有源参数,因此不能使用相同的方法。

你有一些聪明的想法吗,如何测试它?

外部代码将无法执行任何操作来验证值是否是动态生成的。像这样的方法与具体化集合并返回集合的方法之间唯一的实际区别是大规模的内存占用,这在单元测试中很难可靠地进行测试。

通过查看代码,您可以清楚地看出它并没有做到这一点,但您需要以某种非常重要的方式更改实现,以便最终在单元测试中验证这一点(例如编写一个更通用的"Generate"方法,该方法使用委托生成下一个值(。

如果你有某种硬性要求,你的实现有单元测试来验证这些东西,我会写一个Generate方法,通过调用Generate来实现你的Range方法,写一个单元测试来证明Generate在生成序列中的下一个值之前不会调用委托,然后断言CCD_ 5方法由于使用CCD_。不过,我不想在生产代码中这样做,这实际上只是满足需求的一种方式,并为此在可读性和(温和的(性能方面做出一些牺牲。

最新更新