模拟异步方法
Mocking Async Methods
我们正在使用 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
和 return Task
到目前为止一切顺利。
但是 short 运行 async
方法被优化为同步操作。这似乎暗示 TaskCompletionSource
始终是同步的,这似乎也表明上下文处理和可能发生的任何相关问题永远不会发生。
所以如果我们有一些代码在做一些 async
禁忌,比如混合 awaits
、Wait()
和 Result
,这些问题就不会是在单元测试中检测到。
创建一个总是让出控制权的扩展方法会有什么好处吗?像这样的东西:
public async Task<T> ReturnsYieldingAsync<T>(T result)
{
await Task.Yield();
return result;
}
在这种情况下,我们将有一个保证异步执行的方法。
感知到的优势是检测错误的异步代码。例如,它可以在单元测试期间捕获任何死锁或异常吞噬。
我不是 100% 肯定是这样,所以我真的很想听听社区的意见。
当我在四年前 talking about testing asynchronous code 刚开始时 (!),我会鼓励开发人员沿着异步轴测试他们的模拟。也就是说,沿着结果轴(成功,失败)以及异步轴(同步,异步)进行测试。
不过,随着时间的推移,我对此有所放松。在测试我自己的代码时,我实际上只测试同步 success/fail 路径,除非有明确的理由也测试该代码的异步成功路径。我不再为异步故障测试烦恼了。
我们正在使用 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
和 return Task
到目前为止一切顺利。
但是 short 运行 async
方法被优化为同步操作。这似乎暗示 TaskCompletionSource
始终是同步的,这似乎也表明上下文处理和可能发生的任何相关问题永远不会发生。
所以如果我们有一些代码在做一些 async
禁忌,比如混合 awaits
、Wait()
和 Result
,这些问题就不会是在单元测试中检测到。
创建一个总是让出控制权的扩展方法会有什么好处吗?像这样的东西:
public async Task<T> ReturnsYieldingAsync<T>(T result)
{
await Task.Yield();
return result;
}
在这种情况下,我们将有一个保证异步执行的方法。
感知到的优势是检测错误的异步代码。例如,它可以在单元测试期间捕获任何死锁或异常吞噬。
我不是 100% 肯定是这样,所以我真的很想听听社区的意见。
当我在四年前 talking about testing asynchronous code 刚开始时 (!),我会鼓励开发人员沿着异步轴测试他们的模拟。也就是说,沿着结果轴(成功,失败)以及异步轴(同步,异步)进行测试。
不过,随着时间的推移,我对此有所放松。在测试我自己的代码时,我实际上只测试同步 success/fail 路径,除非有明确的理由也测试该代码的异步成功路径。我不再为异步故障测试烦恼了。