我还是很擅长测试,但已经使用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;
}
}
如果我可以更改类中的方法,我会知道如何解决这个问题,但是,在这个例子中,我不能对类本身进行任何更改。我再次不知道是否可以为结果分配任何内容,或者控制窗口,因为两者的变量都是在方法中创建的。
答案 0 :(得分:0)
根据您的要求,这是一种非常常见的模式并且相当容易解决,但首先考虑您可能希望运行哪种类型的测试。
单元测试 - 在只想测试SomeClass
实现的单元测试中,我们不关心其他类的实现,包括SomeDialogWindow
。或者,我们可以编写仅关注SomeDialogWindow
实现的测试,甚至只编写SomeClass::ChangeItem
的实现,而不是其他任何内容。你想要多好?这些测试可以精确地确定您的某些代码被破坏的位置。
验收测试 - 我们构建这些测试以测试一切如何协同工作。他们确实关心不同单位之间的互动,因此会出现单元测试找不到的东西。细微的配置问题,或单元之间更复杂的交互。不幸的是,它们涵盖了大量的代码,所以让你需要更精确的东西来找出问题所在。
在测试驱动的开发项目中,我们可能会编写一个验收测试,以便我们可以看到完成后,我们将逐个编写多个单元测试,每个单元测试用于在代码库中添加一小段功能,并在移动到下一个代码库之前确认它是否有效。
现在该怎么做。只要您能够修改SomeClass
并不是一个巨大的变化,实际上您真正需要的是将virtual
添加到ChangeItem
方法中
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%的代码库,并最终得到快速简单的测试。但是,有时你需要验收测试甚至测试用户界面,然后我们来到一个更复杂的野兽。
对于eaxmple,您的UI将在显示可视元素时包含阻止调用,例如SomeDialogWindow.ShowDialog()
,这些必须在通常称为UI thread
的内容上发生。幸运的是,虽然只有一个线程可以作为UI线程,但如果首先到达那个线程,任何线程都可以是UI线程,但是您需要至少有一个线程显示UI而另一个线程运行测试。您可以从基于Web的测试中窃取模式并创建控制UI的驱动程序类,这些将最终在测试运行线程上执行单击操作和轮询以查看操作是否完整。
如果你需要这些长度,那么在学习如何使用测试框架时,不要从这开始,从简单的东西开始。