C# &引用;调用线程必须是STA,因为许多UI组件都需要此;单元测试期间的异常

C# &引用;调用线程必须是STA,因为许多UI组件都需要此;单元测试期间的异常,c#,.net,unit-testing,sta,visualstudio.testtools,C#,.net,Unit Testing,Sta,Visualstudio.testtools,我有一个名为OptionsWindow的类,它继承自Window,用于从窗口中的选项中拾取。以及处理这些对话框的Dialog类。在我的测试中,我试图模拟从对话框中选择的选项 [TestMethod] public async Task Test() { dialog.Setup(e => e.ShowDialog(It.IsAny<Window>(), It.IsAny<IntPtr>())) .Returns(true)

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

[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....
}
但是,当它试图设置OptionsViewModel的结果时,会出现错误“调用线程必须是STA,因为许多UI组件都需要STA”

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

(im使用Microsoft.VisualStudio.TestTools.UnitTesting btw)

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

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

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

因此,您可以让应用程序定义一个接口,提供它所需的:

public interface IUserInteraction
{
  int ModalOptionsWindow();
}
通过实施:

public sealed class WpfUserInteraction : IUserInteraction
{
  int ModalOptionsWindow()
  {
    OptionsViewModel vm = //.....
    dialog.ShowDialog(new OptionsWindow(vm));
    return vm.Result;
  }
}
界面的具体内容由您决定。通常,我喜欢将ViewModels保留在端口的应用程序端,并且只在端口的UI端具有视图

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



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

您将很难对UI组件进行单元测试。除其他问题外,它们还具有线程关联性(它们只希望从特定线程运行)。避免将可测试逻辑放在UI组件中,而是放在其他普通C#类中(MVVM之类的模式有助于实现这一点)。您最好看看“编码ui测试”功能:
public sealed class WpfUserInteraction : IUserInteraction
{
  int ModalOptionsWindow()
  {
    OptionsViewModel vm = //.....
    dialog.ShowDialog(new OptionsWindow(vm));
    return vm.Result;
  }
}