文章二: 在 C# 中构建 UI 平台 - 通过 UI 动画进行测试






4.20/5 (7投票s)
2005年2月14日
5分钟阅读

52219

927
介绍一种用于支持测试驱动开发的 UI 动画实现。
有意思的数据库技术?
大多数数据库都做了一些很棒的事情:它们会记录。这似乎没什么大不了,直到你考虑到日志包含了定义和修改数据库的命令。再考虑一下,日志可以被回放以重建整个数据库,你就得到了一个远超“大不了”的东西:这是纯粹的天才。像这样的细微天才存在于软件的各个角落。它们常常被伪装成只适用于单一问题的技术,这些技术默默地等待着有人以一种认识到它们背后概念的伟大性的方式重新评估它们。
现在让我们做一些重新评估:如果一个应用程序的工作方式就像一个数据库呢?也就是说,用户所做的一切都会被记录下来,并且日志的定义方式是回放它可以使应用程序恢复到初始用户会话期间达到的相同状态?这样的功能可以用作用户的安全网,在文档丢失或损坏时提供备份。它对技术支持很有用:日志提供了用户所有操作的详细记录。它对培训很有用:动画演示向用户展示如何操作。它在应用程序开发本身中也很有用:每个日志都充当一个测试。
UI 动画:播放器
我们特别关注最后一个变体:测试。考虑到这项技术的众多潜在用途,我们将编写一个简单、抽象的基础设施,它可以支持其中任何一种。这是我们的第一个尝试。
ControlSystem
– 平台的“心脏”,将Windows.Form
事件路由给处理对象。Player
– 运行一组指令,动画化 UI。StopWatch
–System.Threading.Timer
的适配器。Instruction
– 表示用户可以执行的单个操作(MouseMove
、LeftButttonDown
等)。
我们希望 UI 看起来就像用户正在实际操作一样:光标应该移动,按钮应该按下和抬起,控件在拖动时应该改变位置。为了创建这些效果,Player
必须能够访问驱动 ControlSystem
的所有方法。Player
还必须处理计时。虽然我们在验证多个测试时肯定希望快速运行一组指令,但我们也希望以正常速度运行测试,以确保它们正确执行。StopWatch
适配了 System.Threading.Timer
,可以让我们控制解决方案的这部分。最后,Instruction
模拟用户操作,使我们能够以编程方式操作 UI 的部分。让我们编写一个测试,看看这些是如何定义的。
ControlSystem.Player.Add(new MouseMoveInstruction(10, 10));
ControlSystem.Player.Add(new LeftMouseDownInstruction(10, 10));
ControlSystem.Player.Add(new MouseMoveInstruction(100, 100));
ControlSystem.Player.Add(new LeftMouseUpInstruction(100, 100));
默认情况下,Player
将每半秒执行一次指令。我们可以通过在构造函数中传递一个附加参数(Speed
)来更改它。此代码是有效的,并且代表了一种测试。我们还可以做得更“元”,如果你愿意的话,可以通过这些指令的备用构造函数传递要拖动的控件和目标 DropSite
。
ControlSystem.Player.Add(new MouseMoveInstruction(dragSourceControl));
ControlSystem.Player.Add(new LeftMouseDownInstruction(dragSourceControl));
ControlSystem.Player.Add(new MouseMoveInstruction(dragTargetControl));
ControlSystem.Player.Add(new LeftMouseUpInstruction(dragTargetControl));
假设我们具备这些功能,我们就可以通过 Player
运行上一篇文章中介绍的完全相同的测试。由于延迟,这些测试运行得更慢,但它们也测试了鼠标处理系统(与 Windows Form 的鼠标事件处理相同的系统)。最终结果是,这些测试比直接调用类本身更真实地执行了更多的代码。
开发体验
在编写完初始实现并观看第一个测试运行后,我们注意到很难确定测试何时完成。光标停留在某个控件上,让我们想知道:还有一个指令要执行,还是测试已经完成?为了解决这个问题,我们增强了 Monitor,使其包含一个指令日志。每次指令开始时,日志都会添加一个条目,并在指令完成时更新。新的 Monitor。
使用播放器
上一篇文章中的拖动用例被编码为对基础设施对象的直接调用。例如:
protected override void Test()
{
ControlSystem.Mouse.Move(50, 50);
ControlSystem.Mouse.LeftButtonDown(50, 50);
ControlSystem.Mouse.Move(350, 350);
ControlSystem.Mouse.LeftButtonUp(350, 350);
}
这些需要更改为 UI 动画测试,将代码转换为以下内容:
protected override void Test()
{
ControlSystem.Player.Add(new MouseMoveInstruction(50, 50));
ControlSystem.Player.Add(new LeftMouseDownInstruction(50, 50));
ControlSystem.Player.Add(new MouseMoveInstruction(350, 350));
ControlSystem.Player.Add(new LeftMouseUpInstruction(350, 350));
ControlSystem.Player.Play();
}
转换完成后,我们运行测试并进行验证。
我们再次全绿通过。现在我们拥有了足够的基础设施来开始编写控件——这将是下一篇文章的主题。
谢谢!
对于所有一直关注我们这个系列文章的读者,我们表示感谢!你们进展如何?到目前为止,我们已经编写了大约 14,000 行源代码(包括测试和代码)。UI 平台本身大约有 5,500 行代码,分布在 118 个类型中)。我知道,这确实很多。显然,我们无法讨论每一行代码,但请放心:所有基本概念都已涵盖。如果你觉得火车正在没有你的情况下启动,请随时提供反馈或给我们发送电子邮件——我们很乐意讨论你心中所想的任何事情。
下载次数
- UICaseBaseSource.zip - 37.7 Kb。使用测试框架时的起始点解决方案。
- UITestingFrameworkSource.zip - 56.6 Kb。测试框架的源代码。