我有包裹着MessageLoopWorker WebBrowser控件的幾個測試,這裏描述的創建形式,它掛起:WebBrowser Control in a new threadNUnit測試與應用迴路時前
但是,當另一個測試創建用戶控制或形式,測試凍結並無法完成:
[Test]
public async Task WorksFine()
{
await MessageLoopWorker.Run(async() => new {});
}
[Test]
public async Task NeverCompletes()
{
using (new Form()) ;
await MessageLoopWorker.Run(async() => new {});
}
// a helper class to start the message loop and execute an asynchronous task
public static class MessageLoopWorker
{
public static async Task<object> Run(Func<object[], Task<object>> worker, params object[] args)
{
var tcs = new TaskCompletionSource<object>();
var thread = new Thread(() =>
{
EventHandler idleHandler = null;
idleHandler = async (s, e) =>
{
// handle Application.Idle just once
Application.Idle -= idleHandler;
// return to the message loop
await Task.Yield();
// and continue asynchronously
// propogate the result or exception
try
{
var result = await worker(args);
tcs.SetResult(result);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
// signal to exit the message loop
// Application.Run will exit at this point
Application.ExitThread();
};
// handle Application.Idle just once
// to make sure we're inside the message loop
// and SynchronizationContext has been correctly installed
Application.Idle += idleHandler;
Application.Run();
});
// set STA model for the new thread
thread.SetApartmentState(ApartmentState.STA);
// start the thread and await for the task
thread.Start();
try
{
return await tcs.Task;
}
finally
{
thread.Join();
}
}
}
代碼的步驟,以及在除return await tcs.Task;
永遠不會返回。
包裝new Form
到MessageLoopWorker.Run(...)似乎使它更好,但它不適用於更復雜的代碼,不幸的是。我還有很多其他的測試,包括表單和用戶控件,我希望避免包裝到messageloopworker中。
也許MessageLoopWorker可以修復以避免干擾其他測試?
更新:繼@Noseratio的驚人答案我在MessageLoopWorker.Run調用之前重置了同步上下文,現在它運行良好。
更多有意義的代碼:
[Test]
public async Task BasicControlTests()
{
var form = new CustomForm();
form.Method1();
Assert....
}
[Test]
public async Task BasicControlTests()
{
var form = new CustomForm();
form.Method1();
Assert....
}
[Test]
public async Task WebBrowserExtensionTest()
{
SynchronizationContext.SetSynchronizationContext(null);
await MessageLoopWorker.Run(async() => {
var browser = new WebBrowser();
// subscribe on browser's events
// do something with browser
// assert the event order
});
}
當運行測試,而歸零同步上下文WebBrowserExtensionTest塊時它遵循BasicControlTests。通過零位它傳遞良好。
可以保持這樣嗎?
令人驚歎的是,它是超級有用的。謝謝 - 我現在更瞭解它。我已經添加了上下文,希望它會沒事的。 –
@AlexAtNet,如果有幫助,很高興。我仍然不會在MessageLoopWorker的線程之外創建任何WinForms對象。在大多數情況下,它們需要消息循環。至少,如果你這樣做,一定要配置NUnit給你一個STA線程。 – Noseratio