简体   繁体   English

NUnit异步测试+ RequiresSTA =>等待不返回STA线程

[英]NUnit async test + RequiresSTA => await not returning on STA thread

The below code: 下面的代码:

    [RequiresSTA]
    [Test]
    public async Task TestSta()
    {
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId+" - "+Thread.CurrentThread.GetApartmentState());
        // *** await something here ***
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId+" - "+Thread.CurrentThread.GetApartmentState());
        new FrameworkElement();
    }

Yields the following output: 产生以下输出:

9 - STA 9-STA

12 - MTA 12-MTA

And then, throws an InvalidOperationException on new FrameworkElement(). 然后,在新的FrameworkElement()上引发InvalidOperationException。

NUnit supports STA thread creation, now supports async tests, but it seems that it doesnt mix both modes by creating a MTA SynchronizationContext. NUnit支持STA线程创建,现在支持异步测试,但是它似乎没有通过创建MTA SynchronizationContext来混合两种模式。

How do I get this working? 我该如何工作? Any workaround ? 任何解决方法?

You can use the AsyncContext from my AsyncEx library , which was originally written to support async unit tests before the unit test libraries supported them. 您可以使用我的AsyncEx库中AsyncContext ,该最初是为支持async单元测试而编写的,然后再由单元测试库支持。

[RequiresSTA]
[Test]
public Task TestSta()
{
  AsyncContext.Run(async () =>
  {
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId+" - "+Thread.CurrentThread.GetApartmentState());
    // *** await something here ***
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId+" - "+Thread.CurrentThread.GetApartmentState());
    new FrameworkElement();
  });
}

I managed to fix this. 我设法解决了这个问题。 However, I'm sure that a post on stackoverflow would have avoided me quite a headache :) see my answer below. 但是,我敢肯定,关于stackoverflow的帖子会避免我很头疼:)参见下面的答案。

A really good article about SynchronizationContext (click here) just gave me the code (and the knowledge) I required. 一篇关于SynchronizationContext的非常好的文章(单击此处)只是给了我所需的代码(和知识)。

However, I had to tweak it a bit to avoid a deadlock on StaSynchronizationContext disposal, and to propagate the synchronization context inside the worker thread. 但是,我必须对其进行一些调整,以避免在StaSynchronizationContext处置方面出现死锁,并在工作线程内部传播同步上下文。

My test now looks like below: 我的测试现在如下所示:

    [Test]
    [RequiresSTA]
    public async Task DoSomeUITest()
    {
        using (new StaSynchronizationContext())
        {
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " - " + Thread.CurrentThread.GetApartmentState());
            // *** await something here ***
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " - " + Thread.CurrentThread.GetApartmentState());
            new FrameworkElement();
        }
    }

Now outputs: 现在输出:

9 - STA 9-STA

12 - STA 12-STA

... problem solved ! ... 问题解决了 !

Download tweaked code here 在此处下载调整后的代码

* edit and disclaimer * Before having awaited something, your code will be running on the STA thread created by NUnit. *编辑和免责声明*在等待之前,您的代码将在NUnit创建的STA线程上运行。 (thread 9) After the first await, code will be running on the thread created by StaSynchronizationContext (thread 12): even if they're both STA they are not the same thread (线程9)第一次等待后,代码将在StaSynchronizationContext创建的线程(线程12)上运行:即使它们都是STA, 它们也不是同一线程

Beware instanciating controls before await, and using them after. 在等待之前要提防实例化控件,然后再使用它们。 Just needs a bit more tweaking to switch directly to main thread (we could imagine a "using(await StaSynchronizationContext.Create())" which would make us switch on thread 12 at begining) 只是需要更多的调整才能直接切换到主线程(我们可以想象一个“ using(await StaSynchronizationContext.Create())”,这将使我们在开始时打开线程12)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM