面向用户界面(UI)的测试驱动开发(TDD)与功能测试

29
我们知道,TDD意味着“先写测试,然后写代码”。当涉及到单元测试时,这很好,因为你在“单元”内受限。
然而,当涉及到UI时,先编写功能测试就不太合理(对我来说是这样)。这是因为功能测试必须验证一组(可能很长的)功能需求。这通常跨越多个屏幕(页面),前提条件如“已登录”,“最近插入记录”等。 根据维基百科所说:

在需要进行完整的功能测试以确定成功或失败的情况下,使用测试驱动开发是困难的。这些事例包括用户界面、与数据库一起工作的程序以及某些依赖于特定网络配置的程序。

(当然,维基百科并不是“权威”,但这听起来非常合理。)
那么,对于UI,先进行功能测试,然后编写代码,您有什么想法或更好的经验吗?它有效吗?它是否会让人感觉痛苦?

我在许多复杂场景中成功地使用了TDD。 TDD当然不仅限于单元测试。您是否有特定的场景认为使用TDD会很痛苦? - bzlm
7个回答

18

那么规格说明书是在 UI 构建之前编写的,但自动化 UI 测试可能是在 UI 构建之后编写的,对吗? - Steven
这就是BDD的美妙之处,@Steven。在BDD场景中表达的规范是可执行的。 - Boris Pavlović
轻量级的BDD作为TDD策略,可以保持步骤小并帮助定义代码模块化,这是很好的。您可以使用自动生成的快照/回放测试来增强它们,以进行像素完美/签署设计测试。 - ocodo

11

测试 UI 的关键在于 解耦 - UI 的行为与外观实际上是不同的。我们在思维上会有困难,所以可以通过一个练习来理解:拿《俄罗斯方块》这个游戏举例,想象将其从一种平台(比如 PC)移植到另一种(比如 Web)。你可能会感觉一切都不同,需要重新编写所有东西!但实际上它们却是相同的:

  • 游戏规则。
  • 方块下落的速度。
  • 匹配行的逻辑。
  • 选择哪个块。
  • 还有更多...

你已经明白了。唯一改变的是屏幕画面的绘制方式。因此,将 UI 的外观与其功能分离开。这很棘手,而且通常无法完美,但它非常接近。我的建议是最后编写 UI。测试将提供反馈,看看行为是否正常,屏幕将告诉你它是否看起来正确。这种组合提供了我们从 TDD 中寻求的快速反馈,而无需 UI。


顺便提一句,只要你提到一些网站的归属,比如“这是我的领域...”,可能没有人会介意。TDD 是你的专长之一。你也可以在个人资料页面上提供链接。我不会阻止任何人分享有用的信息。 - Drew
俄罗斯方块是一个糟糕的例子。因为它需要文本输入,所以使用单元测试可以获得比应用程序更多的覆盖率。用户界面测试的问题在于输入是点击,输出是像素配置。现在想一想,使用API时,输入是字符串,输出也是字符串。 - Andrew Paul Simmons
1
我认为TDD并不一定意味着单元测试或自动化测试。对于UI,编写您将通过的测试仍然是有意义的,然后运行这些测试,并在可能的情况下依赖于单元测试。 - Andrew Paul Simmons

6

对于功能测试来说,TDD是有意义的。编写一个功能测试,看到它失败,然后将问题分解成部分,为每个部分编写单元测试,为每个部分编写代码,看到单元测试通过,然后你的功能测试应该通过。

以下是Harry Percival在书籍Python TDD开发实战中建议的工作流程(可免费在线获取):

tdd workflow

P.S. 你可以使用 Selenium 等工具自动化功能测试。你可以将它们添加到持续集成周期中,以及单元测试。


3
ATDD非常有用,如果您对UI的行为有很好的掌握,并确保您预先构建的功能测试的变更成本较低。
当然,这样做最大的问题通常是UI没有完全规范化。
例如,如果您正在构建一个产品,并且仍在快速迭代中通过可用性测试进行UI测试并融合反馈,您不希望随着UI的每个小变化而修复功能测试的负担。
考虑到功能测试通常很慢,反馈周期较长,因此很难使它们与UI的变化保持一致。
我在一个产品团队工作,我们也面临这个决定。我的一位同事已经很好地总结了我们的最终方法,详情请点击这里。(免责声明:请忽略那里的特定工具细节。)

2

我曾经用UI进行验收测试驱动开发(Acceptance TDD)。我们会通过xpath查找适当的id来断言是否使用了通用的页眉和页脚。我们还使用xpath来断言数据是否出现在与基本布局和结构所使用的id相对应的正确标签中。我们还会断言输出页面是有效的html 4.01 strict。


1

编程UI测试是一种救赎,当你想要确信你的UI表现如预期时可以使用它。UI测试更接近BDD(行为驱动开发)而不是TDD。但术语不清晰,无论你称呼它们,它们都很有用! 我在使用cucumber方面有着相当不错的经验。我用它来测试Flex应用程序,但它主要用于测试Web应用程序。点击链接!该网站有真正好的方法示例。


1
编程界面测试是必须的,但我认为在编写代码之前不应该编写它们。 - Bozho
这要看情况。我更喜欢在编写代码之前先写出行为描述。这样可以让我逐步进行,保持专注,并且始终有可用的东西。行为描述使得与业务所有者、客户和经理的沟通更加容易。另一个用例是编程可视化组件,这不太属于BDD,但更多地是功能测试。 - Nek
链接失效了。如果我需要一些日本医生的话,这似乎很有用。 - chepe263
链接没问题,请检查一下 :) - Nek
一如既往,这取决于情况。如果TDD UI测试变得过于脆弱,并且它们往往很快就会变得如此。当团队需要进行更改时,他们面临着痛苦/困境。当然,所有的TDD都是如此,只是在UI仍在变化中时更容易受到伤害。对于许多应用程序来说,在从MVP到“最终/黄金”过程中处于变化状态的时间可能是几个月或几年。 - ocodo
在回答发布后的10年里,我的观点多次改变,直到最终汇聚成“这取决于上下文。” - Nek

0

高级UI与直接TDD不兼容。

使用XP实践来指导你。

盲目地遵循TDD(甚至BDD)对于UI来说是不明智的,值得思考这两种实践的根本目标。特别是,我们要避免使用通用的TDD工具来进行UI测试。它们根本没有为这种用例而设计,你最终会将自己绑定在必须像UI代码一样快速迭代的测试上(我称之为UI测试锁定)。

这不是TDD的目的。我们不是为了变慢而这么做,我们是为了更快地前进(并且不破坏任何东西!)。

我们想通过TDD实现以下几点:

  • 代码/算法正确性
  • 完成测试所需的最小代码量
  • 快速反馈
  • 将关注点分离到单元(模块化的被测试部分,不多不少)
  • 以有用/可用的方式规范化说明书
  • 在某种程度上,记录模块/系统的功能(例如提供使用示例)。

我们可以在UI开发中实现这些目标,但如何以及在哪里应用实践是避免测试锁定的重要因素。

将这些原则应用于UI

测试UI有什么好处?我们如何避免测试锁定。

通常的TDD的一个主要好处是,我们可以考虑抽象概念,并设计测试主题的形状,就像我们考虑名称、关系等一样。

对于UI来说,我们想做的很多事情,特别是如果它不是微不足道的,只有当我们可以在屏幕上看到它的表现时,才能有效地进行推理。

编写描述我们期望在UI中看到的内容的测试,特别是数据的测试,可以进行TDD测试。例如,如果我们有一个视图应该显示一个账户余额,一个测试,期望它出现在可以通过某种形式的ID进行寻址的屏幕元素中,是很好的。我们将知道我们的应用程序/视图正在显示预期的内容,在标准UI库元素中。

另一方面,如果我们想创建一个更复杂的UI,并希望将TDD应用于它,我们会遇到一些问题。很可能,如果它足够新颖,我们甚至不知道如何编码它。

为此,我们将使用能够快速反馈但不需要先编写测试的工具进行原型设计和迭代。当我们达到实现清晰的阶段时,我们可以切换到先测试再编写生产代码。

这样可以保持我们的速度。团队中的设计师和产品负责人也可以看到结果,提供意见和调整。

代码应该是良好的形式。一旦我们确定了可能成为生产代码的小部分,我们就可以将它们迁移到测试下。使用TCR或类似方法添加测试,或者简单地使用TDD方法进行重写。记住,一旦它运行正常,您可以应用快照/录制回放测试来避免回归错误。

做有效的事情,保持简单。


附录

关于上述 UI 范围的注释

在任何高级 UI 中,需要找出如何使事情按预期工作。还有可能规格会以极其武断/反复无常的方式发生变化和调整。我们需要确保对这些测试对象应用的任何测试都是灵活的,或者基于工作模型极易重新生成。(回放测试对此非常重要。)

简而言之,良好的开发实践就是代码分离。这将帮助您确保代码至少是可测试的,并且更容易进行调试、维护和推理。

不要成为仪式的奴隶。

这并不能让你正确。只会让你变得更慢。


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