Unit tests that require the STA Thread

csharp xunit tdd wpf 13 comments suggest edit

If you’ve ever written a unit test that instantiates a WPF control, you might have run into one of the following errors:

The calling thread cannot access this object because a different thread owns it.

or

The calling thread must be STA, because many UI components require this.

Prior to xUnit 2.0, we used a little hack to force a test to run on the STA Thread. You simply set the Timeout to 0.

XUnit 1.9

[Fact(Timeout=0 /* This runs on STA Thread */)]
public void SomeTest() {...}

But due to the asynchronous all the way down design of XUnit 2.0, the Timeout property was removed. So what’s a WPF Testing person to do?

Well, I decided to fix that problem by writing two custom attributes for tests:

  • STAFactAttribute
  • STATheoryAttribute

STAFactAttribute is the same thing as FactAttribute but it makes sure the test runs on the STA thread. Same goes for STATheoryAttribute. It’s the same thing as TheoryAttribute.

For example,

[STATheory]
[InlineData(1)]
[InlineData(-1)]
public async Task SomeTest(int someValue)
{
  ...
}

I contributed this code to the xunit/samples repository on GitHub. There’s a lot of great examples in this repository that demonstrate how easy it is to extend XUnit to provide a nice custom experience.

STA Thread

So you might be curious, what is an STA Thread? Stop with the curiosity. Some doors you do not want to open.

But you keep reading because you can’t help yourself. STA stands for Single Threaded Apartment. Apparently this is where threads go when their parents kick them out of the house and they haven’t found a life partner yet. They mostly sit in this apartment, ordering takeout and playing X-Box all day long.

STA Threads come into play when you interop with COM. Most of the time, as a .NET developer, you can ignore this. Unless you write WPF code in which case many of the controls you use depend on COM under the hood.

What is COM? Didn’t I tell you this rabbit hole goes deep? COM stands for Component Object Model. It’s an insanely complicated thing created by Don Box to subjugate the masses. At least that’s what my history book tells me.

Ok, I sort of glossed over the STA part, didn’t I. If you want to know more, check out the Process, Threads, and Apartments article on MSDN.

Apartments are a way of controlling communication between objects on multiple threads. A COM object lives in an apartment and can directly communicate (call methods on) their roommates. Calls to objects in other apartments require involving the nosy busybodies of the object world, proxies.

Single-threaded apartments consist of exactly one thread, so all COM objects that live in a single-threaded apartment can receive method calls only from the one thread that belongs to that apartment. All method calls to a COM object in a single-threaded apartment are synchronized with the windows message queue for the single-threaded apartment’s thread. A process with a single thread of execution is simply a special case of this model.

In WPF, the UI loop is an example of this. UI components must be created on the main application thread and only invoked on that thread. UI components may look pretty, but they’re all single.

For completeness, the alternative to STA is MTA or Multithreaded Apartments. This is where things get really interesting.

Multithreaded apartments consist of one or more threads, so all COM objects that live in an multithreaded apartment can receive method calls directly from any of the threads that belong to the multithreaded apartment. Threads in a multithreaded apartment use a model called free-threading. Calls to COM objects in a multithreaded apartment are synchronized by the objects themselves.

Yes, threads that live in a multithreaded apartment are into this whole “free-threading” lifestyle. Make of it what you will.

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

13 responses

  1. Avatar for xpaulbettsx
    xpaulbettsx November 20th, 2014

    Hah! MTA objects are as if you wrapped every method in a class with "Dispatcher.Invoke(theRealObject.ThatMethod())" - calls are automatically sent to the correct thread, then the result is returned to the caller. At first, it seems like that'd be super convenient, but it's cruise-control for deadlocks and threading issues.

  2. Avatar for Scott Seely
    Scott Seely November 21st, 2014

    You do know that Don Box didn't invent COM, right? He's just famous for being one of the most eloquent educators on how the thing works ;)

  3. Avatar for haacked
    haacked November 22nd, 2014

    Yeah, I was trying to make a joke based on popular perception of COM. :P But if that's _not_ common knowledge, then the joke might just reinforce the myth.

  4. Avatar for Daniel Rose
    Daniel Rose November 24th, 2014

    "In WPF... UI components must be created on the main application thread and only invoked on that thread."
    Actually that is not quite true. WPF UI components can be created on any STA thread. However, they are then bound to that thread and will throw exceptions when accessed from other threads. You can have multiple UI threads with independent(!) objects. Note that this can cause problems due to bugs in third-party UI libraries or in your own code.

  5. Avatar for Brad Westness
    Brad Westness November 24th, 2014

    According to the .NET Framework Capitalization Conventions, shouldn't it be StaFactAttribute?

  6. Avatar for haacked
    haacked November 24th, 2014

    Yeah, probably. It felt weird to not capitalize STA, but it is an acronym that is more than two letters.

  7. Avatar for Hinidu
    Hinidu January 20th, 2015

    Thanks for your code! Is there any particular reason to not provide this functionality via NuGet-package? I think it would be useful for many xUnit 2 users.

  8. Avatar for haacked
    haacked January 20th, 2015

    No reason. Just laziness. I think a lot of the xunit samples would be nice to package up as a NuGet package.

  9. Avatar for bdaniel7
    bdaniel7 April 19th, 2015

    Hi,
    Where do I find this attribute?
    I'm using xUnit 2.0.0.2929 and I'm getting a compilation error.

    Daniel

  10. Avatar for bdaniel7
    bdaniel7 April 19th, 2015

    I mean STAFactAttribute.

  11. Avatar for haacked
    haacked April 20th, 2015

    It's in this repo: https://github.com/xunit/sa...

  12. Avatar for bdaniel7
    bdaniel7 April 20th, 2015

    Well, I tried to add those classes in my project but I couldn't run the tests. The R# runner simply ignored them with this message "Inconclusive: Test not run". And the xunit.console.runner also didn't run any test.

  13. Avatar for haacked
    haacked April 20th, 2015

    Ah, you need to replace the STAExamples namespace with where you put the files.

    For example...

    [XunitTestCaseDiscoverer("STAExamples.STAFactDiscoverer", "STAExamples")]

    Replace "STAExamples" in that attribute with the namespace that matches where you put these classes. I'll talk to the XUnit folks about creating a package to do this later.