【发布时间】:2019-12-18 14:57:45
【问题描述】:
我正在对我的 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";
}
}
同步方法单元测试成功,而异步方法在await之后访问Application.Current.MainWindow时抛出如下异常:
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 应用程序中也会引发相同的异常?
【问题讨论】:
-
在具有异步/等待代码的单线程 WPF 应用程序中不会发生这种情况。但是,由于单元测试是在线程池上运行的,因此由 async/await 创建的任务的默认同步上下文不会是拥有可视元素的调度程序。看到这个类似的帖子stackoverflow.com/questions/39385956/…
-
只是一个愚蠢的问题,为什么你需要通过单元测试来覆盖 UI 逻辑?这是一个 UI 测试的集成范围,IMO
-
我同意。生产代码的编写没有考虑到单元测试。有对
Application.Current.MainWindow(可以去掉)和Application.Current.Dispatcher(可以用接口代替)的引用。我主要担心的是代码不会返回到 WPF 应用程序中的“原始”上下文,这是提供示例的最简单方法。
标签: c# wpf unit-testing async-await