这里的背景是我正在开发一个Windows Store应用程序,由于我是一个谨慎,认真的开发人员,所以我单位测试一切.我很快发现,WSAs不存在ExpectedExceptionAttribute.奇怪,对吧?好吧,没问题!我可以使用扩展方法来复制行为.所以我写道:
public static class TestHelpers { // There's no ExpectedExceptionAttribute for Windows Store apps! Why must Microsoft make my life so hard?! public static void AssertThrowsExpectedException<T>(this Action a) where T : Exception { try { a(); } catch (T) { return; } Assert.Fail("The expected exception was not thrown"); } }
而且,它的作品精美.
所以我继续愉快地写我的单元测试,直到我打了一个异步方法,我想确认在某些情况下抛出异常. “没问题,”我想到自己,“我可以通过一个异步的lambda!”
所以我写了这个测试方法:
[TestMethod] public async Task Network_Interface_Being_Unavailable_Throws_Exception() { var webManager = new FakeWebManager { IsNetworkAvailable = false }; var am = new AuthenticationManager(webManager); Action authenticate = async () => await am.Authenticate("foo","bar"); authenticate.AssertThrowsExpectedException<LoginFailedException>(); }
令人惊讶的是,这会引发运行时错误.它实际上崩溃了测试者!
我重载了我的AssertThrowsExpectedException方法:
public static async Task AssertThrowsExpectedException<TException>(this Func<Task> a) where TException : Exception { try { await a(); } catch (TException) { return; } Assert.Fail("The expected exception was not thrown"); }
我调整了我的测试:
[TestMethod] public async Task Network_Interface_Being_Unavailable_Throws_Exception() { var webManager = new FakeWebManager { IsNetworkAvailable = false }; var am = new AuthenticationManager(webManager); Func<Task> authenticate = async () => await am.Authenticate("foo","bar"); await authenticate.AssertThrowsExpectedException<LoginFailedException>(); }
我的解决方案很好,我只是想知道为什么一切都变成梨形,当我尝试调用异步Action.我猜是因为,就运行时而言,这不是一个Action,我只是把lambda加入到它里面.我知道lambda将很乐意分配给Action或Func< Task>.
解决方法
Action authenticate = async () => await am.Authenticate("foo","bar"); authenticate.AssertThrowsExpectedException<LoginFailedException>();
当你打电话时,这实际上是一个火灾忘记的an async void
method:
try { a(); }
a()立即返回,AssertThrowsExpectedException方法也是如此.同时,一些活动在am.Athenticate内可以在后台继续执行,可能在一个池线程上.究竟发生了什么取决于am.Authenticate的实现,但是当这样的异步操作完成并且它抛出LoginFailedException时,它可能会使您的测试程序崩溃.我不知道单元测试执行环境的同步上下文是什么,但是如果使用默认的SynchronizationContext,那么在这种情况下,异常可能会被抛出不同的线程.
VS2012自动支持异步单元测试,只要测试方法签名是异步任务即可.所以,我想你已经回答了你自己的问题,使用await和Func< T>为你的测试.