C# 如何测试LINQ范围运算符是否延迟?

C# 如何测试LINQ范围运算符是否延迟?,c#,unit-testing,asp.net-core,.net-core,C#,Unit Testing,Asp.net Core,.net Core,我已经实现了某种LINQ范围操作符,并且确实希望有一个测试来验证范围操作符实际上是延迟的 我的范围运算符方法: /// <summary> /// The Range static method, validation part. /// </summary> /// <param name="start">The start.</param> /// <param name="count">Th

我已经实现了某种LINQ范围操作符,并且确实希望有一个测试来验证范围操作符实际上是延迟的

我的范围运算符方法:

/// <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;
    }
}
//
///范围静态法,验证部分。
/// 
///开始。
///伯爵。
/// 
公共静态IEnumerable范围(int start,int count)
{
长最大值=((长)开始)+计数-1;
如果(count<0 | | max>Int32.MaxValue)抛出新的ArgumentOutOfRangeException(nameof(count));
返回范围迭代器(开始,计数);
}
/// 
///范围运算符迭代器。
/// 
///开始。
///伯爵。
/// 
静态IEnumerable RangeIterator(int start,int count)
{
对于(int i=0;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());
    }
//
///负责验证linq运算符是否延迟的类。
/// 
/// 
公共密封类ThrowingExceptionEnumerable:IEnumerable
{
/// 
///方法抛出。
/// 
/// 
公共IEnumerator GetEnumerator()
{
抛出新的InvalidOperationException();
}
/// 
IEnumerator IEnumerable.GetEnumerator()
{
返回GetEnumerator();
}
/// 
///一种方法,用于检查给定对象是否实际使用了延迟执行。
///当函数只调用自身时,它不应该抛出异常
///通过调用和,GetNext()方法应抛出。
/// 
///延迟函数源类型。
///延迟函数结果类型。
///延迟功能(测试中的工作单元)。
公共静态无效资产(
函数(延迟函数)
{
var source=new throwingeExceptionEnumerable();
//不会在此处引发任何异常,因为尚未使用GetEnumerator()方法。
var结果=递延函数(来源);
//即使在这里也不会抛出InvalidOperationException,尽管我们检索了枚举数。
使用var iterator=result.GetEnumerator();
Assert.Throws(()=>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));
}
//
///应检查选择运算符是否延迟。
/// 
[事实]
public void VerifySelectExecutionIsDeferred()验证
{
ThrowingExceptionEnumerable.AssertDeferred(source=>source.Select(x=>x));
}
在为Range操作符编写这样的单元测试时,我遇到的第一个问题是Range实际上是一个静态方法,而不是扩展方法。另外,Range签名没有源参数,因此不能使用相同的方法


您是否有一些聪明的想法,如何对其进行测试?

外部代码无法验证值是否是动态生成的。像这样的方法与具体化集合并返回它的方法之间唯一的实际区别是按比例的内存占用,这很难可靠地进行测试t在单元测试中

您可以清楚地看出,我在查看代码时没有这样做,但是您需要以某种非常重要的方式修改实现,以最终得到一些允许您在单元测试中验证这一点的东西(例如编写一个更通用的“Generate”方法,使用委托生成下一个值)


如果您有某种硬性要求,您的实现需要单元测试来验证这些东西,我会编写这样一个
Generate
方法,通过调用
Generate
来实现
Range
方法,编写一个单元测试来验证
Generate
直到在序列,然后断言
Range
方法延迟执行,因为它使用
Generate
生成其序列。不过,我不想在生产代码中这样做,这实际上只是满足要求的一种方式,并在可读性和(温和)性方面做出一些牺牲为了提高性能。

太好了,你的答案对我来说很清楚。不,我没有这样的要求,我只是在玩TDD和重新实现LINQ,只是很好奇是否有可能进行这样的测试(现在我发现它没有多大意义)。