Getting Started
Installation
Consume this library via its NuGet Package. Click on the badge to find its latest version and the instructions for consuming it that best apply to your project.
Default Fact behavior
/// <summary>
/// Demonstrates that xunit [Fact] behavior is to invoke the test on an MTA thread.
/// </summary>
[Fact]
public async Task Fact_OnMTAThread()
{
ApartmentState expectedApartment = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? ApartmentState.MTA
: ApartmentState.Unknown;
Assert.Equal(expectedApartment, Thread.CurrentThread.GetApartmentState());
await Task.Yield();
Assert.Equal(expectedApartment, Thread.CurrentThread.GetApartmentState());
}
Usage
Portable UI
Best when you need basic UI thread semantics for tests that may run on any OS. You'll get an STA thread on Windows.
/// <summary>
/// Demonstrates that the <see cref="UIFactAttribute"/> can run on all platforms,
/// and emulates a UI thread (not specific to WPF or WinForms) with a <see cref="SynchronizationContext"/>
/// that keeps yielding awaits on the original thread.
/// </summary>
[UIFact]
public async Task UIFact_OnSTAThread()
{
int initialThread = Environment.CurrentManagedThreadId;
Assert.NotNull(SynchronizationContext.Current);
ApartmentState expectedApartment = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? ApartmentState.STA
: ApartmentState.Unknown;
Assert.Equal(expectedApartment, Thread.CurrentThread.GetApartmentState());
await Task.Yield();
Assert.Equal(initialThread, Environment.CurrentManagedThreadId);
Assert.NotNull(SynchronizationContext.Current);
}
WPF
More closely resembles WPF-specific semantics including a WPF-specific SynchronizationContext.
/// <summary>
/// Demonstrates that <see cref="WpfFactAttribute"/> invokes tests expecting an STA thread
/// specifically with a WPF SynchronizationContext.
/// </summary>
[WpfFact]
public async Task WpfFact_OnSTAThread()
{
Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState());
Assert.IsType<DispatcherSynchronizationContext>(SynchronizationContext.Current);
await Task.Yield();
Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState()); // still there
Assert.IsType<DispatcherSynchronizationContext>(SynchronizationContext.Current);
}
WinForms
More closely resembles WinForms-specific semantics including a WinForms-specific SynchronizationContext.
/// <summary>
/// Demonstrates that <see cref="WinFormsFactAttribute"/> invokes tests expecting an STA thread
/// specifically with a WinForms SynchronizationContext.
/// </summary>
[WinFormsFact]
public async Task WinFormsFact_OnSTAThread()
{
Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState());
Assert.IsType<WindowsFormsSynchronizationContext>(SynchronizationContext.Current);
await Task.Yield();
Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState()); // still there
Assert.IsType<WindowsFormsSynchronizationContext>(SynchronizationContext.Current);
}
STA thread
Guarantees the test to run on an STA thread. Applicable only on Windows. Because no SynchronizationContext is applied by default, an async test will resume on a threadpool thread instead of the test thread after a yielding await.
/// <summary>
/// Demonstrates that <see cref="StaFactAttribute"/> invokes tests expecting an STA thread
/// but with no <see cref="SynchronizationContext"/> at all, so any yielding await will
/// return on the threadpool (an MTA thread).
/// </summary>
[StaFact]
public async Task StaWithoutSyncContext()
{
Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState());
Assert.Null(SynchronizationContext.Current);
await Task.Yield();
// Without a single-threaded SynchronizationContext, we won't come back to the STA thread.
Assert.Equal(ApartmentState.MTA, Thread.CurrentThread.GetApartmentState());
Assert.Null(SynchronizationContext.Current);
}