此(n)单元测试失败:
[Test]
public void FooTest()
{
var foo = new Foo();
Assert.That(() => foo.DoSomething(), Throws.InstanceOf<Exception>());
}
这是受测试的课程:
public class Foo : IFoo
{
public async void DoSomething()
{
await DoSomethingAsync();
}
private async Task DoSomethingAsync()
{
await Task.Run(() =>
{
Thread.Sleep(500);
throw new Exception("exception");
});
}
}
如果我更改DoSomething
这个:
public void DoSomething()
{
DoSomethingAsync().Wait();
}
然后测试将通过。我有什么方法可以避免这样做吗?
注意:我无法将DoSomething
的签名更改为public async Task DoSomething()
,因为我无法更改IFoo
界面,因为它来自第三方库
答案 0 :(得分:2)
正如其他评论者所指出的那样,最佳答案是不使用async void
。 async void
的一个主要问题是,从async void
方法检测完成或检索结果(成功/例外)并不容易。因此,单元测试async void
方法真是一种痛苦。
由于您有一个不可更改的IFoo
接口,并且您需要异步等效接口,请考虑添加IFooAsync
接口:
public interface IFooAsync : IFoo
{
Task DoSomethingAsync();
}
然后始终将IFoo.DoSomething
实现为只等待DoSomethingAsync
的显式实现。这类似于我对asynchronous ICommand
implementations采取的方法。
您的第三方库仅使用IFoo
界面,而您的单元测试(和其他代码)可以使用IFooAsync
。
如果由于某种原因,这个解决方案不合适,那么可以通过明确地为它们提供上下文来单元测试async void
方法。我有一个AsyncContext
in my AsyncEx library应该适用于此:
[Test]
public void FooTest()
{
var foo = new Foo();
Assert.That(AsyncContext.Run(() => foo.DoSomething()),
Throws.InstanceOf<Exception>());
}
但是,我建议使用IFooAsync
方法;我认为无论如何它都是一个更清洁的设计。