模拟异步方法

时间:2016-01-14 03:12:12

标签: c# async-await task moq taskcompletionsource

我们正在使用MSTest和Moq编写异步代码的单元测试。

所以我们有一些代码如下:

var moq = new Mock<Foo>();
moq.Setup(m => m.GetAsync())
   .Returns(Task.FromResult(10));

或者在具有更新版Moq

的项目中使用此功能
var moq = new Mock<Foo>();
moq.Setup(m => m.GetAsync())
   .ReturnsAsync(10);

查看ReturnsAsync的Moq实现:

public static IReturnsResult<TMock> ReturnsAsync<TMock, TResult>(this IReturns<TMock, Task<TResult>> mock, TResult value) where TMock : class
{
  TaskCompletionSource<TResult> completionSource = new TaskCompletionSource<TResult>();
  completionSource.SetResult(value);
  return mock.Returns(completionSource.Task);
}

两种方法似乎都是相同的。两者都创建TaskCompletionSource,致电SetResult并返回Task

到目前为止一切顺利。

但是,短时间运行async方法已经过优化,可以同步执行。这似乎意味着TaskCompletionSource始终是同步的,这似乎也表明上下文处理和任何可能发生的相关问题永远不会发生。

因此,如果我们有一些代码正在执行一些async禁忌,例如混合awaitsWait()Result,那么这些问题将无法在单元测试。

创建一个总能产生控制权的扩展方法会有什么好处吗?类似的东西:

public async Task<T> ReturnsYieldingAsync<T>(T result)
{
    await Task.Yield();
    return result;
}

在这种情况下,我们将有一个保证异步执行的方法。

感知的优势是检测错误的异步代码。例如,它可以在单元测试期间捕获任何死锁或异常吞咽。

我不是百分百肯定是这种情况,所以我真的很想听听社区的意见。

1 个答案:

答案 0 :(得分:2)

四年前我开始talking about testing asynchronous code时(!),我鼓励开发人员沿着异步轴测试他们的模拟。也就是说,沿结果轴(成功,失败)以及异步轴(同步,异步)进行测试。

但是,随着时间的推移,我已经放松了。在测试我自己的代码时,我实际上只测试同步成功/失败路径,除非有明确的理由同时测试该代码的异步成功路径。我根本不打扰异步故障测试。