次の方法で共有


Running MSTest In An MTA

Writing deterministic, multi-threaded unit tests may not be your idea of a fun Saturday night, but with a little discipline and knowledge, it can be done, and it sure beats writing multi-dreaded code without the safety net provided by an automated test suite.

In this situation, synchronization becomes really important, and the various WaitHandle classes become some of your best friends. In many test cases, your SUT will probably be kicking off new threads behind the scenes, and you'll need to wait for whatever work they are performing before you carry out the test verification. Waiting for all work to complete sounds just like a job for WaitHandle.WaitAll:

 [TestMethod]
 public void WaitForAllDoStuff()
 {
     MyClass mc1 = new MyClass();
     MyClass mc2 = new MyClass();
     MyClass mc3 = new MyClass();
  
     IAsyncResult ar1 = mc1.BeginDoStuff("ploeh", null, null);
     IAsyncResult ar2 = mc2.BeginDoStuff("fnaah", null, null);
     IAsyncResult ar3 = mc3.BeginDoStuff("ndøh", null, null);
  
     WaitHandle.WaitAll(new WaitHandle[]{ar1.AsyncWaitHandle,
         ar2.AsyncWaitHandle, ar3.AsyncWaitHandle});
  
     // Asserts go here...
 }

In this example, MyClass exposes the DoStuff operation using the Async pattern (or should we say, coding idiom). After waiting for all the processing to complete, the test can gather results by calling EndDoStuff and subsequently perform asserts (not shown). There are other ways to do this, but sometimes, WaitHandle.WaitAll is a nice and convenient construct.

The only problem is that when you try to run this test, it fails with the following error message: Test method Ploeh.Samples.MyLibraryTest.MyClassTest.WaitForAllDoStuff threw exception: System.NotSupportedException: WaitAll for multiple handles on a STA thread is not supported..

This is actually documented behavior on the part of WaitHandle.WaitAll. What may come as a surprise, though, is that MSTest runs in an STA by default.

Fortunately, this is configurable and can be edited in your .testrunconfig file. There's no user interface for this setting, so you will have to open the file with your favorite XML editor and change this element:

 <apartmentState type="System.Threading.ApartmentState">
   <value__ type="System.Int32">1</value__>
 </apartmentState>

By default, the value of this element is 0 (which is equivalent to ApartmentState.STA - the default value of the enumeration), but as you can see, I've changed it to 1 (which is equivalent to ApartmentState.MTA). After saving this change, the test now succeeds.

In Visual Studio 2008, the format of the .testrunconfig file has changed, and this setting is not present at all by default. However, you can add it like this to achieve the same effect:

 <ExecutionThread apartmentState="1" />

Other than that, it works in the same way.

Comments

  • Anonymous
    October 22, 2007
    There was an internal discussion this week about the need to run unit tests under MTA threads, rather

  • Anonymous
    October 22, 2007
    Ever done complex interop testing where you wanted to run as MTA? Needed to call WaitHandle.WaitAll?

  • Anonymous
    October 22, 2007
    Ever done complex interop testing where you wanted to run as MTA? Needed to call WaitHandle.WaitAll?

  • Anonymous
    October 22, 2007
    What is the reason why the MSTest runs in STA by default? I can understand if it is a WinForms app, but for testing a library of code why does the test default to STA?

  • Anonymous
    October 22, 2007
    Performance, they told me.

  • Anonymous
    November 11, 2007
    This post was very helpful to me. Thanks.

  • Anonymous
    January 07, 2008
    This article was helpful to me. Thank you!

  • Anonymous
    June 23, 2008
    Hi, i found your post via MSDN Forums. I set the configuration file like this: <?xml version="1.0" encoding="UTF-8"?> <TestRunConfiguration id="97f0ad10-8fae-403e-b242-ed18618a9b7c" name="TestConWCF" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2006"> <Description>This is a default test run configuration for a local test run.</Description>    <ExecutionThread apartmentState="1"/>   </TestRunConfiguration> But I still getting the same error: FatalExecutionEngineError was detected Message: The runtime has encountered a fatal error. The address of the error was at 0x79eb59e7, on thread 0x79c. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack. I'm working with VS 2008 and I don't know what I'm doing wrong. Thanks

  • Anonymous
    June 25, 2008
    The comment has been removed

  • Anonymous
    July 09, 2008
    The comment has been removed

  • Anonymous
    July 09, 2008
    The comment has been removed

  • Anonymous
    September 20, 2008
    Hi Mark, Thanks for sharing. As I required granular ability to use MTA or STA within a test run I put together a simple helper class to achieve this: http://plainoldstan.blogspot.com/2008/09/run-unit-test-on-mta-thread-vsts-test.html Please let me know if there are drawbacks if you would have a spare minute.

  • Anonymous
    September 22, 2008
    Hi Stanislav Thank you for sharing. I posted a more elaborate comment directly at your post :)

  • Anonymous
    September 29, 2011
    Also have a look here msdn.microsoft.com/.../ms404663.aspx