我有一个自定义Windows窗体控件,其中包含MSTest单元测试。我在这个类中添加了一个异步方法,我需要覆盖它,但新测试总是超时。
经过一些实验,我把问题简化为控制创建和等待。
[TestMethod]
public async Task TestMethod1()
{
Control c = new Control();
await Task.Delay(1);
}
我注意到测试始于SynchronizationContext.Current
设置为null
,然后创建控件后,它会更改为WindowsFormsSynchronizationContext
。如果我强制同步上下文回到null
,那么测试将通过。
[TestMethod]
public async Task TestMethod1()
{
Control c = new Control();
SynchronizationContext.SetSynchronizationContext(null);
await Task.Delay(1);
}
有没有办法从WindowsFormsSynchronizationContext
无阻塞地使用等待?
答案 0 :(得分:7)
Windows窗体控件假定它们在Windows窗体应用程序中运行,而单元测试则不然。这就是你的单元测试挂起的原因。
更具体地说,WinForms控件安装WindowsFormsSynchronizationContext
委托工作到Application.Run
内的Win32消息循环。因此,您的async
方法会查看上下文并将其继续排入该消息循环。但是,没有实际的消息循环,因为您的单元测试不会调用Application.Run
。
最佳解决方案IMO将使用MVVM模式,您不在UI应用程序之外测试UI元素,因此您永远不会遇到这种情况(在MVVM中,您可以对逻辑 UI进行单元测试但不是 literal UI)。使用MVVM方法,您的代码将如下所示:
[TestMethod]
public async Task TestMethod1()
{
ViewModel vm = new ViewModel();
await vm.MethodAsync();
}
但是如果你想对实际的UI元素进行单元测试,你可以使用Async CTP中包含的类似Windows Forms Context的类型:
[TestMethod]
public async Task TestMethod1()
{
await WindowsFormsContext.Run(async () =>
{
Control c = new Control();
await Task.Delay(1);
});
}