为什么我的异步单元测试会抛出InvalidOperationException而同一个同步测试却不会?

时间:2019-12-18 14:57:45

标签: c# wpf unit-testing async-await

我正在对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应用程序中也可以引发相同的异常?

1 个答案:

答案 0 :(得分:1)

  

我的印象是,等待后的语句返回到“原始”上下文(已知Application.Current.MainWindow)。

只要您调用SynchronizationContext时有任何要捕获的上下文,它就会在捕获的await上继续。

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

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