我的代码中的计算经过了很好的测试,但是因为有太多的 GUI 代码,我的整体代码覆盖率低于我想要的。是否有关于单元测试 GUI 代码的指南?它甚至有意义吗?
例如,我的应用程序中有图表。我一直无法弄清楚如何自动化图表的测试。需要人眼 AFAIK 来检查图表是否正确。
(我正在使用 Java Swing)
我的代码中的计算经过了很好的测试,但是因为有太多的 GUI 代码,我的整体代码覆盖率低于我想要的。是否有关于单元测试 GUI 代码的指南?它甚至有意义吗?
例如,我的应用程序中有图表。我一直无法弄清楚如何自动化图表的测试。需要人眼 AFAIK 来检查图表是否正确。
(我正在使用 Java Swing)
MVP 和 MVC 之类的设计通常会尝试从实际 GUI 中抽象出尽可能多的逻辑。一篇非常受欢迎的文章是Michael Feathers 的“The Humble Dialog Box”。就我个人而言,我在尝试将逻辑从 UI 中移出时有着不同的经历——有时它工作得很好,而在其他时候,它的麻烦多于它的价值。不过,这有点超出了我的专业领域。
当然,答案是使用 MVC 并将尽可能多的逻辑移出 GUI。
话虽如此,很久以前我从一位同事那里听说,当 SGI 将 OpenGL 移植到新硬件时,他们进行了一系列单元测试,这些单元测试会将一组基元绘制到屏幕上,然后计算帧缓冲区的 MD5 和。然后可以将该值与已知的良好哈希值进行比较,以快速确定 API 是否按像素准确。
你可以试试UISpec4J是一个开源的功能和/或单元测试库,用于基于 Swing 的 Java 应用程序......
有Selenium RC,它将自动测试基于 Web 的 UI。它将记录动作并重播它们。您仍然需要逐步完成与 UI 的交互,因此这对覆盖率没有帮助,但它可以用于自动构建。
您可以尝试使用Cucumber和Swinger为 Swing GUI 应用程序用简单的英语编写功能验收测试。Swinger 在后台使用 Netbeans 的 Jemmy 库来驱动应用程序。
Cucumber 允许您编写如下测试:
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!"
看看这个Swinger 视频演示,看看它的实际效果。
这里有一些提示:
尝试从 GUI 中删除尽可能多的代码(具有控制器和模型对象),这样您就可以在没有 GUI 的情况下测试它们。
对于图形,您应该测试您提供给生成图形的代码的值。
测试是一种艺术形式。我同意应尽可能删除逻辑 GUI。然后我们可以将单元测试集中在那里。像其他任何测试一样,测试是为了降低风险。你并不总是需要测试所有东西,但很多时候最好的办法是在不同的区域分解不同的测试。
另一个问题是你真正想在 UI 层测试什么。UI 测试是最昂贵的测试,因为它通常需要更长的时间来创建、维护并且它是最脆弱的。如果您在尝试绘制线之前测试逻辑以知道坐标是否正确,那么您具体测试的是什么?如果要测试用红线绘制的图形。你能给它一个预定的坐标并测试某些像素是红色还是非红色?正如上面建议的位图比较工作,Selenium 但我的主要关注点不是过度测试 GUI,而是测试有助于创建 UI 的逻辑,然后关注 UI 的哪些部分中断或可疑,并关注少数测试那里。
您可以使用JFCUnit来测试您的 GUI,但图形可能更具挑战性。我曾多次拍摄我的 GUI 快照并自动将其与以前的版本进行比较。虽然这不提供实际测试,但它会在自动构建未能产生预期输出时提醒您。
我从您的问题中收集到的是,您正在寻找一种自动化方法来详细测试您的 GUI 行为,您给出的示例是测试曲线是否实际绘制正确。
单元测试框架提供了一种进行自动化测试的方法,但我认为你想要做的测试是复杂的集成测试,它验证大量类的正确行为,其中你的 GUI 工具包/库的类,你应该不想测试。
您的选择很大程度上取决于您使用的平台/工具包/框架:例如,使用 Qt 作为其 GUI 框架的应用程序可以使用 Squish 来自动化其测试。您验证测试结果一次,随后自动执行的测试将结果与验证结果进行比较。
Swing 和 Ajax 的Window Licker
我的 GUI 测试方法在不断发展,行业共识也在不断发展。但我认为一些关键技术正在开始出现。
我使用这些技术中的一种或多种,具体取决于情况(例如,它是什么类型的 GUI、需要多快构建它、最终用户将是谁等)。
手动测试。在处理代码时始终运行 GUI,并确保它与代码同步。您在工作时手动测试和重新测试您工作的部分,在代码和正在运行的应用程序之间切换。每次完成一些重要的工作时,都要对应用程序的整个屏幕或区域进行全面测试,以确保没有回归。
单元测试。您为功能或 GUI 行为的小单元编写测试。例如,您的图表可能需要根据“基础”颜色计算颜色的不同深浅。您可以将此计算提取到函数并为其编写单元测试。您可以在 GUI 中搜索这样的逻辑(尤其是可重用逻辑)并将其提取到谨慎的函数中,这样可以更容易地进行单元测试。甚至可以以这种方式提取和测试复杂的行为——例如,可以将向导中的一系列步骤提取到函数中,并且单元测试可以验证给定输入是否返回了正确的步骤。
组件浏览器。您创建了一个“资源管理器”屏幕,其唯一作用是展示构成您的 GUI 的每个可重用组件。此屏幕为您提供了一种快速简便的方法来直观地验证每个组件是否具有正确的外观和感觉。组件浏览器比手动浏览整个应用程序更有效,因为 A)您只需验证每个组件一次,并且 B)您不必深入应用程序来查看组件,您只需查看和立即验证。
自动化测试。您编写一个与屏幕或组件交互的测试,模拟鼠标点击、数据输入等,断言应用程序在这些操作下正确运行。这可以用作额外的备份测试,以捕获其他测试可能遗漏的潜在错误。我倾向于为最容易损坏和/或非常关键的 GUI 部分保留自动化测试。如果有什么东西坏了,我想尽早知道的部分。这可能包括高度复杂的交互式组件,这些组件容易损坏或重要的主屏幕。
差异/快照测试。您编写的测试只是将输出捕获为屏幕截图或 HTML 代码,并将其与之前的输出进行比较。这样,每当输出发生变化时,您都会收到提醒。如果您的 GUI 的视觉方面很复杂和/或可能发生变化,则差异测试可能很有用,在这种情况下,您需要快速直观地反馈给定更改对整个 GUI 的影响。
与其粗暴地使用每一种可能的测试,我更喜欢根据我正在做的事情来挑选和选择测试技术。因此,在一种情况下,我将提取一个简单的函数并对其进行单元测试,但在另一种情况下,我将向组件资源管理器中添加一个组件,等等。这取决于具体情况。
我还没有发现代码覆盖率是一个非常有用的指标,但其他人可能已经发现了它的用途。
我认为第一个衡量标准是错误的数量和严重程度。您的首要任务可能是拥有一个正常运行的应用程序。如果应用程序正常运行,则应该很少或没有错误。如果有很多或严重的错误,那么可能是您没有进行测试,或者您的测试无效。
除了减少错误之外,还有其他措施,例如性能、可用性、可访问性、可维护性、可扩展性等。这些会有所不同,具体取决于您正在构建的应用程序类型、业务、最终用户等。
这一切都基于我的个人经验和研究,以及Ham Vocke对UI 测试的精彩文章。
据我所知,这很复杂,而且真的取决于语言——许多语言都有自己的测试 GUI 的方式,但是如果你真的需要测试 GUI(而不是模型/GUI 交互),你通常必须模拟实际用户点击按钮。例如,Eclipse 中使用的 SWT 框架提供了SWTBot,JFCUnit已经提到过,Mozilla 在 XUL 中有自己的模拟方式(从我在他们的博客上看到的内容来看,这些测试似乎很脆弱)。
有时您必须截取屏幕截图并测试像素完美渲染(我相信 Mozilla 这样做是为了检查正确渲染的页面) - 这需要更长的设置,但可能是您需要的图表。这样,当您更新代码并且测试中断时,您必须手动检查图像是否失败,或者您改进了图形渲染代码以生成更漂亮的图形并需要更新屏幕截图。
如果您使用的是 Swing,FEST-Swing可用于驱动您的 GUI 和测试断言。它使测试诸如“如果我单击按钮 A,应显示对话框 B”或“如果我从下拉菜单中选择选项 2,所有复选框都应取消选中”之类的测试变得非常简单。
您提到的图形场景并不容易测试。仅仅通过创建和显示它们(也许用 FEST 驱动它们)就很容易获得 GUI 组件的代码覆盖率。然而,做出有意义的断言是困难的部分(并且没有有意义的断言的代码覆盖是一种自欺欺人的练习)。你如何测试图表没有倒置或太小?
我认为您只需要接受 GUI 的某些方面无法通过自动化单元测试进行有效测试,您将不得不以其他方式对其进行测试。
测试 GUI 库不是您的工作。因此,您可以逃避检查屏幕上实际绘制的内容的责任,而是检查小部件的属性,相信库可以准确地表示所绘制的内容。