Running xUnit.net Tests on Specific Threads for WPF and Other UI Tests

Sometimes when you write a test with xUnit.net (or other testing frameworks) you may run into problems if UI technologies are involved. This usually relates to the fact that the test must execute using a specific threading model such as single-threaded apartment (STA).

For example suppose you had a WPF app that you wanted to add tests for.

The XAML looks like:

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <TextBlock FontSize="42" Text="{Binding Path=Greeting}" />
    </Grid>
</Window>

And the simple quick and dirty view model class looks like:

namespace WpfApp1
{
    public class MainWindowViewModel
    {
        public string Greeting { get; set; }
    }
}

And  in the MainWindow constructor we set the data context:

public MainWindow()
{
    InitializeComponent();

    var vm = new MainWindowViewModel { Greeting = "Hi there!" };
    DataContext = vm;
}

(This is a very simple demo code with no change notifications etc.)

If you wanted to write an xUnit.net test that instantiates an instance of MainWindow, such as:

[Fact]
[UseReporter(typeof(DiffReporter))]
public void RenderWithViewModel()
{
    var sut = new MainWindow();
    var vm = new MainWindowViewModel { Greeting = "Good day!" };
    sut.DataContext = vm;

    // Test rendering, e.g. using Approval Tests
    WpfApprovals.Verify(sut);
}

If you run this, the test will fail with: System.InvalidOperationException : The calling thread must be STA, because many UI components require this.

Note: this test is using Approval Tests (e.g. [UseReporter(typeof(DiffReporter))]) to render the UI into an image file for approval, you can learn more about Approval Tests with my Pluralsight course. Approval Tests is no related to the threading model requirements.

To enable this test to run you need to instruct xUnit to run the test using an apartment model process (“STA thread”).

Luckily Andrew Arnott has done all the hard work for us and created some custom xUnit.net attributes that allow us to specify what thread/synchronization context to use for a test.

Once the Xunit.StaFact NuGet package has been installed into the test project you can replace the standard [Fact] attribute with [StaFact]. The test will now execute without error:

using ApprovalTests.Reporters;
using ApprovalTests.Wpf;
using Xunit;

namespace WpfApp1.Tests
{
    public class MainWindowShould
    {
        [StaFact]
        [UseReporter(typeof(DiffReporter))]
        public void RenderWithViewModel()
        {
            var sut = new MainWindow();
            var vm = new MainWindowViewModel { Greeting = "Good day!" };
            sut.DataContext = vm;

            // Test rendering, e.g. using Approval Tests
            WpfApprovals.Verify(sut);
        }
    }
}

There are also a number of other attributes such as [WinFormsFact] for use with Windows Forms apps, check out the entire list of attributes in the docs.

If you use this library make sure to say a thankyou to Andrew on Twitter  :)

SHARE:

Add comment

Loading