C# 确认正在等待任务
我想测试以下代码:C# 确认正在等待任务,c#,.net,unit-testing,task-parallel-library,async-await,C#,.net,Unit Testing,Task Parallel Library,Async Await,我想测试以下代码: private Task _keepAliveTask; // get's assigned by object initializer public async Task EndSession() { _cancellationTokenSource.Cancel(); // cancels the _keepAliveTask await _logOutCommand.LogOutIfPossible(); await _keepAliveTas
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
返回的任务是否等待\u 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
}
单元测试看起来(简化):
但这是不可能的(异步同步)。此外,它仍然存在以前方法的问题
使用Task.whalll(…)
是一种(有效的)性能改进,但会带来更大的复杂性:
-如果不隐藏第二个异常(当两个异常都失败时),就很难纠正错误
-允许并行执行
由于性能不是这里的关键,我希望避免额外的复杂性。此外,前面提到的问题,即它只是将(验证)问题转移到EndSession
方法的调用者,也适用于这里
观察效果而不是验证调用
现在当然不用“单元”测试方法调用等,我可以随时观察效果。也就是说:只要\u keepAliveTask
没有结束结束会话
任务
也不能结束。但是,由于我不能无限期地等待,一个人不得不满足于超时。测试应该很快,所以像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();
}
[测试]
public void EndSession_MustWaitForKeepAliveTaskToEnd()
{
var keepAlive=new TaskCompletionSource();
_cancelableLoopingTaskFactory
.Setup(x=>x.Start(It.IsAny(),It.IsAny())
.Returns(keepAlive.Task);
_testee.StartSendingKeepAlive();
_testee.EndSession()
.Wait(时间跨度从毫秒(20))
.Should().BeFalse();
}
但我真的不喜欢这种方法:
- 难懂
- 不可靠
- 或者——当它相当可靠时——它需要很长时间(单元测试不应该)
如果您只想验证结束会话
是否正在等待\u keepAliveTask
(并且您确实可以完全控制\u 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
方法的GetAwaiter
方法,该方法返回带有IsCompleted
、OnCompleted
和GetResult
的内容,因此您可以使用虚拟等待来确保\u keepAliveTask
正在等待:
_keepAliveTask = new MyAwaitable();
EndSession();
_keepAliveTask.IsAwaited.Should().BeTrue();
如果你使用一些模拟框架,你可以让Task
的GetAwaiter
返回我们的MyAwaiter
使用TaskCompletionSource
并在已知时间设置其结果
确认在设置结果之前,等待结束会话尚未完成
验证设置结果后,等待结束会话是否已完成
简化版本可能如下所示(使用nunit):
如果您不相信您的单元测试框架能够正确处理异步,那么您可以在waitTask中设置一个completed标志,并在最后进行检查。比如:
var waitTask = Task.Run(async () =>
{
await Task.Delay(1);
await keepAliveTask;
});
bool completed = false;
var waitTask = Task.Run(async () =>
{
await Task.Delay(1);
await keepAliveTask;
completed = true;
});
// { .... }
// at the end of the method
Assert.IsTrue(completed);
如果\u keepAliveTask
被取消并等待,它应该抛出一个操作取消异常,不是吗?我不太确定你到底想做什么。您想确保等待LogOutIfPossible
?您实际想要测试什么?EndSession
完成了吗?@l3arnonEndSession
只完成一次\u keepAliveTask
completes@YuvalItzchakov是的,我认为这是一个可能的解决办法。让任务返回结果(或者在没有结果的情况下抛出异常),并验证“包装”任务是否返回相同的结果(抛出异常)。非常感谢。你使用什么样的测试框架?MS Test支持异步测试方法。我将尝试一下。仍然需要确保自定义的MyAwaitable
正确实现,不过……这基本上就是我描述的观察效果,而不是验证调用。这不可靠。为了达到足够的可靠性,测试必须花费很长时间。在单元测试中,等待一秒(或更长)是不可接受的。除非你能将它们并行运行,否则问题就不会那么严重。
_keepAliveTask = new MyAwaitable();
EndSession();
_keepAliveTask.IsAwaited.Should().BeTrue();
[Test]
public async Task VerifyTask()
{
var tcs = new TaskCompletionSource<bool>();
var keepAliveTask = tcs.Task;
// verify pre-condition
Assert.IsFalse(keepAliveTask.IsCompleted);
var waitTask = Task.Run(async () => await keepAliveTask);
tcs.SetResult(true);
await waitTask;
// verify keepAliveTask has finished, and as such has been awaited
Assert.IsTrue(keepAliveTask.IsCompleted);
Assert.IsTrue(waitTask.IsCompleted); // not needed, but to make a point
}
var waitTask = Task.Run(async () =>
{
await Task.Delay(1);
await keepAliveTask;
});
bool completed = false;
var waitTask = Task.Run(async () =>
{
await Task.Delay(1);
await keepAliveTask;
completed = true;
});
// { .... }
// at the end of the method
Assert.IsTrue(completed);