首页 > 解决方案 > “调用线程必须是STA,因为许多UI组件都需要这个”单元测试期间的异常

问题描述

我有一个名为 OptionsWindow 的类,它继承自 Window ,用于从窗口中的选项中进行选择。还有一个处理这些对话框的对话框类。在我的测试中,我试图模拟从对话框中选择的选项。

[TestMethod]
public async Task Test()
{
    dialog.Setup(e => e.ShowDialog(It.IsAny<Window>(), It.IsAny<IntPtr>()))
                .Returns(true)
                .Callback<Window, IntPtr>((w, ip) => {
                    if (w.DataContext != null && w.DataContext is OptionsViewModel ovm)
                        ovm.Result = -1;
                    });
    await tester.ShowWindow();
    //assert....
}

然后在正在测试的课程中,我有这些方法。

public async Task ShowWindow()
{
    var res = ShowDialog();
    //do other stuff...
}

private int ShowDialog()
{
    OptionsViewModel vm = //.....
    dialog.ShowDialog(new OptionsWindow(vm));
    return vm.Result;
}

但是,当它尝试设置 OptionsViewModel 的结果时,我收到错误“调用线程必须是 STA,因为许多 UI 组件需要这个”。

在手动测试期间,一切正常,没有线程问题,所以我不知道为什么我在这里得到这些......任何帮助都很棒。谢谢

(我使用 Microsoft.VisualStudio.TestTools.UnitTesting 顺便说一句)

标签: c#.netunit-testingstavisualstudio.testtools

解决方案


在我的测试中,我试图模拟从对话框中选择的选项。

通常,如果编写测试很困难,则表明代码应该设计得更好。

在这种情况下,直接依赖 UI 组件并不理想。有一种称为端口和适配器(又名六边形架构,又名清洁架构)的模式在这里会有所帮助。总之,您从应用程序的角度定义接口,然后让小型适配器对象实现这些接口。

所以你可以让应用程序定义一个接口来提供它需要的东西:

public interface IUserInteraction
{
  int ModalOptionsWindow();
}

有一个实现:

public sealed class WpfUserInteraction : IUserInteraction
{
  int ModalOptionsWindow()
  {
    OptionsViewModel vm = //.....
    dialog.ShowDialog(new OptionsWindow(vm));
    return vm.Result;
  }
}

界面涵盖的具体内容取决于您。通常,我喜欢将我的 ViewModel 保留在端口的应用程序端,并且只在端口的 UI 端有视图。

一旦你有一个接口,注入IUserInteraction并让你的代码调用它。之后,简化了单元测试。


但是,如果您处于遗留代码场景中,需要在重构之前编写测试,那么您可以对 UI 代码进行单元测试。这并不容易。请参阅WpfContextWindowsFormsContext在此旧存档中查看创建 STA 线程并从单元测试中抽取消息的方法。


推荐阅读