c# – 验证正在等待任务

前端之家收集整理的这篇文章主要介绍了c# – 验证正在等待任务前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有以下代码,我想测试:
private Task _keepAliveTask; // get's assigned by object initializer

public async Task EndSession()
{
    _cancellationTokenSource.Cancel(); // cancels the _keepAliveTask
    await _logoutCommand.logoutIfPossible();
    await _keepAliveTask;
}

重要的是EndSession Task只在`_keepAliveTask’结束后才结束.但是,我很难找到一种方法来可靠地测试它.

问题:如何对EndSession方法进行单元测试并验证EndSession返回的任务是否等待_keepAliveTask.

出于演示目的,单元测试可能如下所示:

public async Task EndSession_MustWaitForKeepAliveTaskToEnd()
{
    var keepAliveTask = new Mock<Task>();
    // for simplicity sake i slightly differ from the other examples
    // by passing the task as method parameter

    await EndSession(keepAliveTask);

    keepAliveTask.VerifyAwaited(); // this is what i want to achieve
}

进一步的标准:
– 可靠的测试(总是在实现正确时通过,在实现错误时总是失败)
– 不能超过几毫秒(毕竟这是一个单元测试).

我已经考虑了几个备选方案,我将在下面记录:

非异步方法

如果没有对_logoutCommand.logoutIfPossible()的调用,那将非常简单:我只是删除异步并返回_keepAliveTask而不是等待它:

public Task EndSession()
{
    _cancellationTokenSource.Cancel();
    return _keepAliveTask;
}

单元测试看起来(简化):

public void EndSession_MustWaitForKeepAliveTaskToEnd()
{
    var keepAliveTask = new Mock<Task>();
    // for simplicity sake i slightly differ from the other examples
    // by passing the task as method parameter

    Task returnedTask = EndSession(keepAliveTask);

    returnedTask.Should().be(keepAliveTask);
}

但是,有两个论点反对这个:

>我有多个需要等待的任务(我正在考虑Task.WhenAll down down)
>这样做只会将责任转移到等待EndSession调用者的任务.仍然需要在那里测试它.

非异步方法,异步同步

当然,我可以做类似的事情:

public Task EndSession()
{
    _cancellationTokenSource.Cancel(); // cancels the _keepAliveTask
    _logoutCommand.logoutIfPossible().Wait();
    return _keepAliveTask;
}

但这是禁止的(同步异步).此外,它仍然存在先前方法的问题.

使用Task.WhenAll(…)的非异步方法

是(有效的)性能改进,但引入了更多的复杂性:
– 如果不隐藏第二个异常(两个都失败时)很难做到
– 允许并行执行

由于性能不是关键,我想避免额外的复杂性.此外,前面提到的问题,它只是将(验证)问题移动到EndSession方法调用者也适用于此.

观察效果而不是验证电话

现在当然不是“单位”测试方法调用等.我总能观察到效果.这是:只要_keepAliveTask没有结束,EndSession任务也不能结束.但由于我不能无限期地等待,因此我必须暂时停止.测试应该很快,所以5秒的超时是不行的.所以我做的是:

[Test]
public void EndSession_MustWaitForKeepAliveTaskToEnd()
{
    var keepAlive = new TaskCompletionSource<bool>();
    _cancelableLoopingTaskFactory
        .Setup(x => x.Start(It.IsAny<ICancelableLoopStep>(),It.IsAny<CancellationToken>()))
        .Returns(keepAlive.Task);

    _testee.StartSendingKeepAlive();

    _testee.EndSession()
            .Wait(TimeSpan.FromMilliseconds(20))
            .Should().BeFalse();
}

但我真的不喜欢这种方法

>难以理解
>不可靠
>或 – 当它非常可靠时 – 需要很长时间(单元测试不应该).

解决方法

如果你想要的只是验证EndSession正在等待_keepAliveTask(并且你真的可以完全控制_keepAliveTask)那么你可以创建自己的等待类型而不是在等待时对任务进行处理,并检查:
public class MyAwaitable
{
    public bool IsAwaited;
    public MyAwaiter GetAwaiter()
    {
        return new MyAwaiter(this);
    }
}

public class MyAwaiter
{
    private readonly MyAwaitable _awaitable;

    public MyAwaiter(MyAwaitable awaitable)
    {
        _awaitable = awaitable;
    }

    public bool IsCompleted
    {
        get { return false; }
    }

    public void GetResult() {}

    public void OnCompleted(Action continuation)
    {
        _awaitable.IsAwaited = true;
    }
}

因为所有你需要等待的东西是有一个GetAwaiter方法返回一些IsCompleted,OnCompleted和GetResult,你可以使用虚拟等待来确保正在等待_keepAliveTask:

_keepAliveTask = new MyAwaitable();
EndSession();
_keepAliveTask.IsAwaited.Should().BeTrue();

如果你使用一些模拟框架,你可以使Task的GetAwaiter返回我们的MyAwaiter.

原文链接:https://www.f2er.com/csharp/100169.html

猜你在找的C#相关文章