鉴于.NET Framework 4.5后.NET增长了对 async/await 的支持,本文讨论的异步内容均基于async/awaitbash
客户端界面开发,多线程是逃不了的话题,而多线程的加入势必对程序的稳定性带来挑战,单元测试就显得更为重要。相对于同步代码的测试,多线程单元测试有更多细节须要注意。多线程
假设某一天你运气很差,须要为相似以下的方法补充单元测试:异步
public static bool Changed;
public static async void ChangeAsync()
{
await Task.Run(() =>
{
Task.Delay(1000);
Changed = true;
});
}
复制代码
你发现,要测试此方法须要用一些奇葩的方式,好比:async
[TestMethod()]
public void ChangeAsyncTest_OriginalFalse_ChangeToTrue()
{
AsyncClient.Changed = false;
AsyncClient.ChangeAsync();
Thread.Sleep(1100);
Assert.IsTrue(AsyncClient.Changed);
}
复制代码
显然,这种延时等待是极其恶心的,若是ChangeAsync方法返回的不是void而是Task,咱们就能够愉快的await了:单元测试
[TestMethod()]
public async Task ChangeAsyncTest_OriginalFalse_ChangeToTrue()
{
AsyncClient.Changed = false;
await AsyncClient.ChangeAsync();
Assert.IsTrue(AsyncClient.Changed);
}
复制代码
须要特别注意的是,在异步单元测试方法中也必须返回Task,这是MSTest的约定,不然这个测试方法没法运行起来。(实际上MSTest也须要使用返回的Task来收集异常,关于这部分更多内容能够参见Async/Await最佳实践)测试
在测试程序是否按照预期的抛出了异常,咱们经常会用ExpectedException,这家伙有一个问题,它是对整个测试方法的方法体作捕获,也就是说测试方法中的非action代码抛出了异常依然可以被ExpectedException捕获,这就形成潜在的bug,为了解决此问题,在MSTest V2以前每每须要写一些辅助方法,但MSTest V2断言库中增长了Assert.ThrowsExceptionAsync和Assert.ThrowsException,能够精确的定位在哪段代码中抛出了异常。假设咱们的被测代码跟下面相似:spa
public static async Task ChangeAsync()
{
await Task.Run(() =>
{
throw new InvalidOperationException();
});
}
复制代码
测试代码能够这样写:线程
[TestMethod()]
public async Task ChangeAsyncTest_ThrowInvalidOperationException()
{
await Assert.ThrowsExceptionAsync<InvalidOperationException>(async () =>
{
await AsyncClient.ChangeAsync();
});
}
复制代码
在moq中,异步方法的mock也是极其简单的,假设有这样的接口:code
public interface ITextReader
{
Task<string> ReadTextAsync();
}
复制代码
测试代码中mock其返回结果能够有以下两种写法:接口
var mockTextReader = new Mock<ITextReader>();
//能够这样
mockTextReader.Setup(x => x.ReadTextAsync()).Returns(async ()=>await Task.FromResult("mockValue"));
//也能够这样
mockTextReader.Setup(x => x.ReadTextAsync()).ReturnsAsync(()=> "mockValue");
复制代码
2017-11-30 15:26:34