我有以下代码需要测试:
重要的是,
问题:如何对
为了演示目的,单元测试可能看起来像这样:
进一步的标准包括: - 可靠的测试(当实现正确时始终通过,当实现错误时始终失败) - 不能超过几毫秒(毕竟这是一个单元测试)。
我已经考虑了几种备选方案,如下所述:非
单元测试看起来会像这样(简化):
使用
我有以下代码需要测试:
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
}
进一步的标准包括: - 可靠的测试(当实现正确时始终通过,当实现错误时始终失败) - 不能超过几毫秒(毕竟这是一个单元测试)。
我已经考虑了几种备选方案,如下所述:
非async
方法
如果没有对 _logOutCommand.LogOutIfPossible() 的调用,它就会很简单:我只需删除async
并将_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(...)
的非async
方法
虽然这是(有效的)性能提升,但会引入更多的复杂性: - 没有隐藏第二个异常(当两者都失败时)很难正确处理 - 允许并行执行
由于性能在这里不是关键,我想避免额外的复杂性。此外,先前提到的问题,即它只是将(验证)问题移交给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();
}
但是我真的非常不喜欢这种方法:
- 难以理解
- 不可靠的
- 或者,当它相当可靠时,需要很长时间(这不应该是单元测试的特点)。
_keepAliveTask
被取消并等待,它应该抛出OperationCancelledException
,不是吗?我不太确定你实际上想做什么。你想确保LogOutIfPossible
被等待吗? - Yuval ItzchakovEndSession
是否完成? - i3arnonEndSession
只有在_keepAliveTask
完成后才会完成。 - BatteryBackupUnit[Test]public async Task MyTestMethdo()
的 NUnit,并且我经常使用它。 - BatteryBackupUnit