使用SpecFlow捕获对话框窗口

本文关键字:对话框 窗口 SpecFlow 使用 | 更新日期: 2023-09-27 18:26:32

我对测试还是相当陌生的,但已经使用SpecFlow几个月了。我不完全确定我要问的是否可能,但也许有人会提出解决这个问题的建议。

简介:我的功能文件调用了一个方法,该方法创建了一个存储在该方法中创建的变量中的对话框窗口。然后,用户需要填写对话框窗口(基本上是选择一个文件,然后单击"确定")。该方法的其余部分依赖于对话框窗口提供的信息。

问题:由于窗口是在方法中创建的,结果存储在当时创建的变量中,因此我无法将信息提供到变量中。但为了完成我的行为测试,我需要提供这些信息。

示例代码:

功能文件:

Given I initialize the class
And I click on change selected item

步骤文件:

[Given(@"I initialize the class")]
public void GivenIInitializeTheClass()
{
    DoStuff();
    SomeClass testClass = new SomeClasee();
}
[Given(@"IClickOnChangeSelectedItem")]
public void GivenIClickOnChangeSelectItem()
{
    testClass.ChangeItem();
}

来自类的方法:

public void ChangeItem()
{
    var window = new SomeDialogWindow();
    var result = window.ShowDialog();
    if (result.HasValue && result.Value)
    {
        NewItem = window.SelectedItem;
    }
}

如果我可以更改类中的方法,我就会知道如何进行此操作,但在本例中,我不能对类本身进行任何更改。同样,我不知道是否可以为结果分配任何内容,或者控制窗口,因为两者的变量都是在方法中创建的。

使用SpecFlow捕获对话框窗口

根据您想要做的事情,这是一种非常常见的模式,而且很容易解决,但首先让我们考虑您可能想要运行什么样的测试。

单元测试-在只想测试SomeClass实现的单元测试中,我们不关心包括SomeDialogWindow在内的其他类的实现。或者,我们可以编写只关心SomeDialogWindow的实现的测试,甚至只关心SomeClass::ChangeItem的实现,而不关心其他内容。你想走多远?这些测试可以准确地定位某些代码的损坏位置。

验收测试-我们构建这些测试是为了测试所有东西是如何协同工作的。他们确实关心不同单元之间的交互,因此会显示单元测试找不到的东西。微妙的配置问题,或单元之间更复杂的交互。不幸的是,它们涵盖了大量的代码,所以你需要更精确的东西来找出问题所在。

测试驱动的开发项目中,我们可能会编写一个验收测试,这样我们就可以看到何时完成,然后我们会一次编写多个单元测试,每个单元测试用于向代码库添加一小段功能,并在转到下一个之前确认它是否有效。

现在了解如何操作。只要你能够修改SomeClass,这并不是一个巨大的变化,事实上,你真正需要的只是在ChangeItem方法中添加virtual来制作

public virtual void ChangeItem()
...

现在,当你测试这个方法时,你可以用一个不同的实现来替换它

namespace xxx.Tests
{
  public class TestableSomeClass : SomeClass
  {
    public Item TestItem {get;set;}
    public override void ChangeItem()
    {
      NewItem = TestItem;
    }
  }
}

这是一种非常常见的模式,称为存根。我们已经将ChangeItem中的功能简化为最基本的功能,因此它只是其最初意图的一个极小的存根。我们不再测试ChangeItem,只测试代码的其他部分。

事实上,这种模式非常常见,有一些库可以帮助我们模拟函数。我倾向于使用一个叫Moq的,现在它看起来是这样的。

//Given
var desiredItem = ...
Mock<SomeClass> myMock = new Mock<SomeClass>();
myMock.Setup(x=>x.ChangeItem).Returns(desiredItem);
var testClass = myMock.Object;
//When 
testClass.ChangeItem();
//Then
testClass.NewItem.ShouldEqual(....);

您会注意到,在这两个例子中,我们都去掉了代码库中的GUI部分,这样我们就可以专注于您的功能。我个人建议使用这种方法,可以获得90%的代码库覆盖率,并最终实现快速而简单的测试。然而,有时你需要验收测试,甚至测试UI,然后我们就遇到了一个更复杂的问题。

对于eaxmple,当UI显示视觉元素(如SomeDialogWindow.ShowDialog())时,它将包括阻塞调用,而这些调用必须发生在通常称为UI thread的对象上。幸运的是,虽然只有一个线程可以是UI线程,但如果它先到达那里,任何线程都可以是UI螺纹,但您需要至少有一个线程显示UI,另一个线程运行测试。您可以从基于web的测试中窃取模式,并创建控制UI的驱动程序类,这些驱动程序类最终将在测试运行线程上执行单击操作和轮询,以查看操作是否完成。

如果你需要达到这些长度,那么当你学习如何使用测试框架时,不要从这个开始,从简单的东西开始。