gpt4 book ai didi

c# - 验证正在等待任务

转载 作者:太空狗 更新时间:2023-10-29 19:43:57 25 4
gpt4 key购买 nike

我想测试以下代码:

private Task _keepAliveTask; // get's assigned by object initializer

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

重要的是 EndSession 任务仅在 `_keepAliveTask' 结束后结束。但是,我正在努力寻找一种可靠的测试方法。

问题:如何对 EndSession 方法进行单元测试并验证 EndSession 返回的 Task 等待_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() 的调用,那将非常简单:我只需删除 asyncreturn _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)
  • 这样做只会将等待任务的责任转移给 EndSession 的调用者。仍然必须在那里进行测试。

非异步方法,同步优于异步

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

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

但这是行不通的(同步优于异步)。此外,它仍然存在以前方法的问题。

异步方法使用Task.WhenAll(...)

是一个(有效的)性能改进但引入了更多的复杂性: - 很难在不隐藏第二个异常的情况下做对(当两者都失败时) - 允许并行执行

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

观察效果而不是验证调用

现在当然不是“单元”测试方法调用等。我总能观察到效果。即:只要 _keepAliveTask 还没有结束 EndSession Task 也不能结束。但是因为我不能无限期地等待,所以必须接受超时。测试应该很快,所以 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),那么您可以创建您自己的可等待类型而不是 Task 信号在等待时检查:

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;
}
}

因为您需要await something 是有一个GetAwaiter 方法返回somethingIsCompletedOnCompletedGetResult 您可以使用虚拟等待来确保正在等待 _keepAliveTask:

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

如果您使用一些模拟框架,您可以改为让 TaskGetAwaiter 返回我们的 MyAwaiter

关于c# - 验证正在等待任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28024923/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com