如何对 GUI 进行单元测试?

我的代码中的计算是经过良好测试的,但是由于有这么多 GUI 代码,我的整体代码覆盖率比我想要的要低。对于单元测试 GUI 代码有什么指导方针吗?这说得通吗?

比如,我的应用里有图表。我还没弄明白如何自动化测试这些图表。它需要一个人的眼睛,AFAIK,来检查图表是否正确。

(我正在使用 Java Swing)

73965 次浏览

像 MVP 和 MVC 这样的设计通常试图从实际的 GUI 中抽象出尽可能多的逻辑。一篇非常受欢迎的文章是由迈克尔 · 费瑟斯撰写的 “卑微的对话框”。就我个人而言,我曾有过尝试将逻辑从 UI 中移出的混合体验——有时它工作得非常好,有时它带来的麻烦超过了它的价值。不过这有点超出了我的专业范围。

以下是一些小贴士:

尝试从 GUI 中删除尽可能多的代码(包括控制器和模型对象) ,这样您就能够在没有 GUI 的情况下测试它们。

对于图形,应该测试提供给生成图形的代码的值。

您可以使用 JFCUnit来测试 GUI,但是图形可能更具挑战性。有几次,我对 GUI 进行了快照,并自动将其与以前的版本进行了比较。虽然这并不提供实际的测试,但是如果自动构建不能产生预期的输出,它会提醒您。

您可以尝试使用 UISpec4J作为基于 Swing 的 Java 应用程序的开源函数和/或单元测试库..。

硒 RC,它将自动测试一个基于网络的用户界面。它会记录动作并重放它们。您仍然需要遍历与 UI 的交互,因此这对覆盖率没有帮助,但是可以用于自动构建。

据我所知,这非常复杂,而且确实取决于语言——许多语言都有自己测试 GUI 的方式,但是如果你真的需要测试 GUI (相对于模型/GUI 交互) ,你通常需要模拟一个实际的用户点击按钮。例如,Eclipse 中使用的 SWT 框架提供了 SWTBot,已经提到了 JFCUnit,Mozilla 有自己的 XUL 模拟方法(从他们的博客上看,这些测试似乎相当脆弱)。

有时你需要截屏,并测试像素完美的渲染(我相信 Mozilla 这样做是为了检查正确渲染的页面)-这需要更长的设置,但可能是你需要的图形。这样,当您更新代码和测试中断时,您必须手动检查图像是否真的出现故障,或者您改进了图形呈现代码以生成更漂亮的图形,并且需要更新屏幕截图。

当然,答案是使用 MVC 并尽可能多地将逻辑移出 GUI。

话虽如此,我很久以前从一位同事那里听说,当 SGI 将 OpenGL 移植到新硬件上时,他们有一大堆单元测试,这些测试会在屏幕上绘制一组基元,然后计算帧缓冲区的 MD5和。然后,可以将此值与已知的良好散列值进行比较,以快速确定 API 是否每像素准确。

如果您正在使用 Swing,Fest-Swing对于驱动 GUI 和测试断言非常有用。它使得测试像 如果我点击按钮 A,对话框 B 就会显示出来或者 “如果我从下拉菜单中选择了选项2,那么所有的复选框都将被取消选择”这样的东西变得非常简单。

您提到的图形场景不太容易测试。通过创建和显示 GUI 组件(或者使用 FEST 驱动它们) ,很容易获得 GUI 组件的代码覆盖率。然而,做出有意义的断言是困难的部分(没有有意义的断言的代码覆盖是一种自欺欺人的练习)。如何测试该图表是否没有上下颠倒绘制,或绘制得太小?

我认为您必须接受 GUI 的某些方面不能通过自动化单元测试进行有效测试,而必须以其他方式进行测试。

我从您的问题中得出的结论是,您正在寻找一种自动化的方法来详细测试 GUI 行为,您给出的示例是测试曲线是否实际上正确绘制。

单元测试框架提供了一种进行自动化测试的方法,但是我认为您想要进行的测试类型是复杂的集成测试,用于验证大量类的正确行为,其中包括您的 GUI 工具包/库中的类,您不应该测试这些类。

您的选择在很大程度上取决于所使用的平台/工具包/框架: 例如,使用 Qt 作为 GUI 框架的应用程序可以使用 Squish 自动化其测试。您只需验证一次测试结果,随后自动执行的测试将结果与已验证的结果进行比较。

您可以尝试使用 黄瓜摇摆人为 Swing GUI 应用程序以简单的英语编写功能验收测试。Swinger 在引擎盖下使用 Netbeans 的 Jemmy 库来驱动这个应用程序。

黄瓜允许您编写这样的测试:

 Scenario: Dialog manipulation
Given the frame "SwingSet" is visible
When I click the menu "File/About"
Then I should see the dialog "About Swing!"
When I click the button "OK"
Then I should not see the dialog "About Swing!"

看看这个 摇摆舞视频演示,看看它的运作情况。

Windows Licker for Swing & Ajax

考试是一门艺术。我同意逻辑应该尽可能地删除 GUI。然后我们可以将单元测试集中在那里。就像其他测试一样,都是为了降低风险。您并不总是需要测试所有的东西,但是很多时候最好的方法是在不同的区域打破不同的测试。

另一个问题是,您真正想要在 UI 层测试什么。UI 测试是最昂贵的测试,因为它通常需要更长的时间来创建、维护,而且它是最脆弱的。如果您在尝试绘制直线之前测试逻辑以知道坐标是正确的,那么您具体要测试什么?如果要测试带有红线的图形,则绘制。你能给它一个预先确定的坐标和测试,如果某些像素是红色或不红色?正如上面建议的位图比较工作,Selenium 但我的主要焦点将不是过度测试 GUI,而是测试将有助于创建 UI 的逻辑,然后集中在 UI 的哪一部分出现了问题或可疑,并集中在那里的一些测试。

测试 GUI 库不是您的工作。因此,您可以避免检查屏幕上实际绘制的内容的责任,而是检查小部件的属性,相信库能够准确地表示所绘制的内容。

我的 GUI 测试方法正在发展,业界的共识也是如此。但我认为一些关键技术正在开始显现。

根据具体情况,我会使用一种或多种这样的技术(例如,它是什么类型的 GUI,需要多快构建它,最终用户是谁,等等)。

  1. 手动测试。在处理代码时始终运行 GUI,并确保 GUI 与代码同步。您可以在工作时手动测试和重新测试所处理的部分,并在代码和正在运行的应用程序之间进行切换。每次完成一些重要的工作时,都要对应用程序的整个屏幕或区域进行全面测试,以确保不会出现回归。

  2. 单元测试。为函数或小的 GUI 行为单元编写测试。例如,您的图可能需要计算不同的色度的颜色基础上的’基础’颜色。您可以将此计算提取到一个函数并为其编写一个单元测试。您可以在 GUI 中搜索这样的逻辑(特别是可重用的逻辑) ,并将其提取到离散函数中,这样可以更容易地进行单元测试。即使是复杂的行为也可以通过这种方式提取和测试——例如,向导中的一系列步骤可以提取到函数中,单元测试可以验证给定的输入返回了正确的步骤。

  3. 组件资源管理器。你创建了一个“浏览器”屏幕,它的唯一作用就是展示组成 GUI 的每个可重用组件。这个屏幕为您提供了一个快速简单的方法,可视化地验证每个组件具有正确的外观和感觉。组件浏览器比手动浏览整个应用程序更有效率,因为 A)你只需要验证每个组件一次,B)你不必深入到应用程序来查看组件,你只需要立即查看和验证它。

  4. 自动化测试。编写一个与屏幕或组件交互的测试,模拟鼠标点击、数据输入等,断言应用程序在进行这些操作时正确运行。这可以作为额外的备份测试,用于捕获其他测试可能遗漏的潜在错误。我倾向于将自动化测试保留给 GUI 中最容易崩溃和/或非常关键的部分。我想尽早知道是否有什么东西坏了。这可能包括高度复杂的交互式组件,这些组件容易破坏或重要的主屏幕。

  5. 差异/快照测试。您编写一个测试,该测试简单地将输出捕获为屏幕快照或 HTML 代码,并将其与前一个输出进行比较。这样,只要输出发生变化,就会向您发出警报。如果 GUI 的视觉方面很复杂并且/或者容易改变,那么 Diff 测试可能会很有用,在这种情况下,您需要快速且可视化的反馈,以了解给定的改变对整个 GUI 的影响。

与其笨拙地使用每一种可能的测试,我更喜欢根据我正在做的事情来挑选测试技术。因此,在一种情况下,我将提取一个简单的函数并对其进行单元测试,但在另一种情况下,我将向组件浏览器添加一个组件,等等。这要看情况。

我还没有发现代码覆盖率是一个非常有用的指标,但是其他人可能已经发现了它的用途。

我认为第一个指标是错误的数量和严重性。您的首要任务可能是拥有一个正常运行的应用程序。如果应用程序运行正确,应该很少或没有错误。如果存在许多或严重的错误,那么可以推测,您要么没有进行测试,要么测试没有效果。

除了减少错误,还有其他措施,如性能、可用性、可访问性、可维护性、可扩展性等。这些会有所不同,这取决于您所构建的应用程序的类型、业务、最终用户等等。

这些都是基于我个人的经验和研究,以及 哈姆 · 沃克UI 测试的大量报道。