首页 > 解决方案 > 为什么我的异步单元测试会抛出 InvalidOperationException 而同一个同步测试不会?

问题描述

我正在对我的 WPF 项目的一部分进行单元测试,该项目使用async代码并Application.Current.MainWindow设置主窗口的标题。

public class ClassUnderTest
{
    // Get and apply title async
    public async Task GetAndApplyWindowTitleFromDbAsync()
    {
        string windowTitle = await GetWindowTitleFromDbAsync();
        Application.Current.MainWindow.Title = windowTitle;
    }

    public async Task<string> GetWindowTitleFromDbAsync()
    {
        await Task.Delay(2000);
        return "Async Window Title";
    }

    // Get and apply title sync
    public void GetAndApplyWindowTitleFromDb()
    {
        Application.Current.MainWindow.Title = "Sync Window Title";
    }
}

同步方法的单元测试成功,而异步方法在之后访问时抛出以下Application.Current.MainWindow异常await

System.InvalidOperationException:调用线程无法访问此对象,因为不同的线程拥有它。

[TestClass]
public class TestClass
{

    [TestMethod]
    public void SyncMethodWithUICodeTest()
    {
        InitializeApplication();

        Assert.AreEqual("Window Title", Application.Current.MainWindow.Title);

        var classUnderTest = new ClassUnderTest();

        classUnderTest.GetAndApplyWindowTitleFromDb();

        // Test succeeds
        Assert.AreEqual("Sync Window Title", Application.Current.MainWindow.Title);
    }

    [TestMethod]
    public async Task AsyncMethodWithUICodeTest()
    {
        InitializeApplication();

        Assert.AreEqual("Window Title", Application.Current.MainWindow.Title);

        var classUnderTest = new ClassUnderTest();

        await classUnderTest.GetAndApplyWindowTitleFromDbAsync();

        // throws InvalidOperationException
        Assert.AreEqual("Async Window Title", Application.Current.MainWindow.Title);
    }

    private void InitializeApplication()
    {
        if (Application.Current == null)
        {
            var app = new Application();
            var window = new Window();
            app.MainWindow = window;
        }

        Application.Current.MainWindow.Title = "Window Title";
    }
}

我的印象是,之后的声明await返回到“原始”上下文(Application.Current.MainWindow已知的地方)。

为什么async单元测试会抛出异常?单元测试的原因是特定的还是可以在我的 WPF 应用程序中引发相同的异常?

标签: c#wpfunit-testingasync-await

解决方案


我的印象是 await 之后的语句返回到“原始”上下文(Application.Current.MainWindow已知的位置)。

SynchronizationContext如果在您调用时有任何要捕获的上下文,它会在捕获时恢复await

在 MSTest 单元测试的上下文中,System.Threading.SynchronizationContext.Current返回null,因此一旦完成就没有上下文可以恢复Task

您可以尝试SynchronizationContext通过调用来设置SynchronizationContext.SetSynchronizationContext. WPF 应用程序System.Windows.Threading.DispatcherSynchronizationContext默认使用 a。


推荐阅读