我该如何对GUI进行单元测试?

162

我的代码中的计算经过了充分测试,但由于有大量GUI代码,我的整体代码覆盖率比我想要的低。是否有关于单元测试GUI代码的指导方针?这甚至有意义吗?

例如,在我的应用程序中有图表。我无法找到如何自动化测试图表的方法。据我所知,需要人眼来检查图表是否正确。

(我正在使用Java Swing)


1
有一篇由Martin Fowler撰写的关于不同GUI架构的优秀文章(http://www.martinfowler.com/eaaDev/uiArchs.html)。它描述了在单元测试方面进行GUI架构权衡。 - Roman Hwang
1
现在,这个问题最好在http://programmers.stackexchange.com上提问(并且可能因为太宽泛而不适合在Stack Overflow上讨论),但是旧问题无法迁移。除了问题是否适合在此处讨论之外,该问题仍然是一个有趣且困难的问题。 - Mark Amery
1
带有一些GUI代码和JUnit的示例将非常好。 - Witold Kaczurba
2
我会说只是放松,不要烦恼。投入单元测试的努力并不总是产生净生产力。 - codeulike
旧问题仍然存在,您可以使用Jubula在GUI中测试流程。 - Abi
14个回答

73

像MVP和MVC这样的设计通常试图尽可能地将逻辑抽象出实际的GUI。关于这个问题,Michael Feathers 的文章 "谦卑的对话框" 是非常流行的一篇文章。就我个人而言,尝试将逻辑移出UI会有好有坏,有时效果很好,而另外一些时候则得不偿失。但是这也超出了我的专业领域。


有时它运行得非常好,而有时候它带来的麻烦比它的价值还要大。这很奇怪,因为大多数新语言都倾向于采用MVC架构...即将逻辑从GUI中分离出来(模型-视图)... - Patrick Desjardins
16
尽管如此,演示逻辑将会在GUI中。有时,这可能远非简单易懂。 - Andrea
17
镜像:谦逊的对话框 - c24w
Martin Fowler对这个概念的处理(如果您的网络像我的一样过滤archive.org):http://www.martinfowler.com/eaaDev/uiArchs.html#HumbleView - Christopher Berman
“谦逊的对话框”并不描述真正的MVP,而是它的早期草稿,即文档视图。 - ivan_pozdeev

42

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

话虽如此,我曾经从一位同事那里听说,当SGI将OpenGL移植到新硬件时,他们有一堆单元测试会将一组基本图形绘制到屏幕上,然后计算帧缓冲区的MD5值。通过与已知的好哈希值进行比较,可以快速确定API是否按像素精确。


3
我喜欢这种方法。设置和维护都很麻烦,但它将为我提供所需的回归测试能力。 - Steve McLeod
7
你没有抓住重点。OpenGL是一个像素精确的图形渲染API。像Qt这样的GUI应用程序框架将高度依赖于系统chrome,因此哈希帧缓冲区不是一个好的方法。 - Bob Somers
1
一个MD5?密码哈希与此有什么关系?安全性如何涉及?哈希,好的,对于像素完美的图形来说是个好主意,但是密码哈希?您可以使用更快的非密码哈希,其碰撞概率可以忽略不计。您确定它是密码哈希而不仅仅是哈希吗?(除此之外,在这个并行计算的时代,不能并行化的哈希算法[用于非密码目的]应该受到怀疑) - SyntaxT3rr0r
25
在这种非安全相关应用中,你会推荐哪种哈希函数?相较于MD5,可以期待多少性能提升?请进行翻译,不添加解释或其他内容。 - Lèse majesté
@phriendtj,在这种情况下,密码哈希算法的雪崩效应是非常无意义的。说它很重要就像说计算机不能区分(2^32) - 1(2^32) - 2之间的差异一样。也就是说,至少根据我理解你的评论,你必须看到雪崩效应才能检测到不同的值。这不应该是这种情况。任何旧的哈希算法(包括非加密选项)都应该适用于这种用例,因为重复的机会和抵抗预像攻击的能力应该是无关紧要的。话虽如此,我会使用MD5。 - Spencer D
显示剩余4条评论

10

你可以尝试使用UISpec4J, 它是一个开源的功能和/或单元测试库,适用于基于Swing的Java应用程序...


2
我已经开始使用UISpec4J大约一个月的时间,在Java Swing GUI的需求验证测试中。到目前为止,我非常喜欢它。 - Bassam
https://github.com/UISpec4J/UISpec4J 表示您的链接是官方网站,但我觉得它看起来并不那么正式。我只看到一个关于视频博客的博客。 - lucidbrot
1
@lucidbrot 这是以前的链接:http://web.archive.org/web/20090304192541/http://www.uispec4j.org/ - username.ak

7

测试是一种艺术形式。我同意逻辑应该尽可能地从GUI中移除。我们可以将重点放在单元测试上。像任何其他测试一样,测试的目的是为了降低风险。你并不总是需要测试所有内容,但很多时候最好将不同的测试分开进行。

另一个问题是,你真正想要在UI层面测试什么。UI测试是最昂贵的测试,因为通常需要更长时间来创建和维护,并且它也是最脆弱的。如果你在尝试绘制线之前测试逻辑以确保坐标正确,你具体在测试什么?如果你想要测试一个带有红线的图表,可以给它设置预定坐标并测试某些像素是否为红色或非红色吗?如上所述,位图比较、Selenium都可以使用,但我的主要重点是不要过度测试GUI,而是测试将有助于创建UI的逻辑,然后专注于UI的哪个部分会出现故障或可疑,并在那里进行少量的测试。


7

有一个名为Selenium RC的工具,它可以自动化测试基于Web的用户界面。它会记录操作并重放它们。您仍然需要手动执行与您的UI的交互,因此它不会帮助覆盖范围,但它可用于自动构建。


7
您可以尝试使用CucumberSwinger来编写针对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视频演示以查看它的实际效果。


6

以下是一些提示:

尽量从GUI中删除大部分代码(使用控制器和模型对象),这样您就可以在没有GUI的情况下测试它们。

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


3
我的GUI测试方法正在不断发展,行业共识也在不断演变。但我认为一些关键技术开始出现。
根据情况(例如GUI类型、构建速度、最终用户等),我使用其中一种或多种技术。
  1. 手动测试。 在编码过程中,您始终保持GUI运行,并确保它与代码同步。您会在工作时手动测试和反复测试您所工作的部分,不断在代码和运行应用程序之间切换。每当您完成一些重要的工作时,您会对整个屏幕或应用程序区域进行全面测试,以确保没有回归。

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

  3. 组件浏览器。 您创建一个“浏览器”屏幕,其唯一作用是展示组成您GUI的每个可重用组件。此屏幕可快速轻松地验证每个组件是否具有正确的外观和感觉。组件浏览器比手动浏览整个应用程序更有效,因为A)您只需验证每个组件一次,B)您不必深入应用程序中查看组件,只需立即查看并验证它。

  4. 自动化测试。 您编写一个与屏幕或组件交互的测试,模拟鼠标单击,数据输入等,断言应用程序在进行这些操作时是否正常运行。这可以作为额外的备份测试,捕获其他测试可能会错过的潜在错误。我倾向于将自动化测试保留给最容易出错和/或非常关键的GUI部分。其中我希望尽早知道是否出现问题的部分。这可能包括高度复杂的交互式组件容易出错或重要的主要屏幕。

  5. 差异/快照测试。 您编写一个简单的测试,将输出捕获为屏幕截图或HTML代码,并将其与先前的输出进行比较。这样,每当输出发生变化时,您都会收到提醒。如果您的GUI的视觉方面很复杂和/或易于更改,则差异测试可能很有用,在这种情况下,您希望快速获得有关给定更改对整个GUI的影响的视觉反馈。

与其笨拙地使用各种可能的测试,我更喜欢根据我正在处理的事物选择测试技术。因此,在某些情况下,我会提取一个简单的函数并对其进行单元测试,但在另一些情况下,我会添加一个组件到组件浏览器中等等。这取决于具体情况。
我没有发现代码覆盖率是一个非常有用的指标,但其他人可能已经找到了它的用途。
我认为第一个指标是错误的数量和严重程度。你的首要任务可能是拥有一个正确运行的应用程序。如果应用程序能够正确运行,则应该很少或没有错误。如果存在许多或严重的错误,则可能是你没有进行测试或者你的测试不够有效。
除了减少错误之外,还有其他措施,例如性能、可用性、可访问性、可维护性、可扩展性等。这些将因你正在构建的应用程序类型、业务、最终用户等而异。
这一切都基于我的个人经验和研究,以及Ham Vocke关于UI Tests的出色论述。

3

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


3
从您的问题中我了解到,您正在寻找一种自动化的方式来详细测试GUI行为,您提供的示例是测试曲线是否正确绘制。单元测试框架提供了一种自动化测试的方式,但我认为您想要进行的测试类型是复杂的集成测试,可以验证多个类的正确行为,其中包括GUI工具包/库的类,您不应该希望测试这些类。
您的选择很大程度上取决于您使用的平台/工具包/框架:例如,使用Qt作为其GUI框架的应用程序可以使用Squish来自动化其测试。您只需验证测试结果一次,随后自动执行的测试将与验证结果进行比较。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接