原文地址:http://www.donnfelker.com/tdd-your-ui-layer/
最近,当我在Caster.IO课程上发布了一篇关于使用TDD来驱动UI开发的文章后,twitter上便就此展开了讨论。
那么问题在哪呢?
一些人认为你不能用一个功能测试的框架比如Espresso来通过测试驱动UI层的开发。
但我不这么认为。
为什么?在我详细深入之前,我觉得有必要重复声明一下什么是TDD。
什么是TDD?
TDD就是测试驱动开发。TDD是一个软件开发流程。
关键字:流程。
TDD的过程由5个步骤组成:
1.添加一个测试,任何一个新功能,升级,修复等等都从编写覆盖新代码和改变的测试开始。
2.运行所有测试并观察是否有新的测试失败了(通常为了节省时间,我会单独运行新测试来提速,然后再回过头来运行所有测试来进行回归测试)
3.编写代码(这是你本来需要做的或想要去做的—比如实现新功能等等)
4.运行测试—同样,通常,我会单独运行新的测试来加速进程。如果通过了,我会运行所有的测试来检查是否有错误。
5.重构—所有测试都通过之后,现在你有了一个安全的网络来捕获你代码中的错误。这个时候你可以开始重构你的代码了。
然后,每一个新的功能,更新,修复等等都重复这些步骤。你可能要一遍遍的重复这些步骤。一个“快乐路径”的测试(当所有功能都如你希望的那样),一个所有边缘情况的,一个空值的路径,一个灾难性的错误等。你通常会以测试所有不同的希望(或意外的结果)代码路径而结束。
所以,如何TDD你的UI层?
当我们浏览这个例子的时候,让我牢记TDD的全过程。
这意味着首先编写你的测试,甚至在编写UI元素之前。这样你会使用Espresso,与Junit一起使用的Android UI测试框架。
第一步:创建测试
我会创建一个这样的测试:
正如你所看到的,buttonx这个id根本不存在(所以它是红色的)。正因如此我需要实现这个按钮,所以接下来我们会看见这些:
现在buttonx这个id可以被找到了(它的颜色变了,现在我们也可以定位到它的定义了)。我是如何实现它的并不重要,这里我们关心的是TDD的过程。我可以成功的编译。好了,现在我可以进入TDD的第二步了:
第二步:运行测试
我运行测试并希望它失败,因为文本“Hello from buttonx”并不存在。如果它没有失败(意味着它通过了)那么我就有问题了,我需要深挖原因。让我们假设它失败了,所以我准备进入第三步。
第三步:编写代码
这里,我要编写代码为了让这些代码能够通过测试(它们可能会是为buttonx添加点击事件让它在屏幕某处输出“Hello from buttonx”)。然后进入第四步。
第四步:运行测试
我们运行测试来确保它们和其他测试都能通过。这是TDD必需的过程。你会想运行所有的测试(通常至少是所有和这些组件屏幕等相关的测试)来找到任何因为新代码添加而产生的回归问题的出现。在所有测试都通过后,我会继续第五步。
第五步:重构
到了这里我会整理代码来让它变的更好。可能会让它们更加private,final或者提取方法等。或许我发现了一段我可以提高设计模式的代码。如果是这样,比如可能我在新的类中重构了某些逻辑,我会加入新的TDD测试过程到新的重构中。什么情况下会发生这样的情况或者为什么呢?依我看来可能是我在一些地方使用了相同的代码,我可以将它们提取到另一个类中。因此,我可能想要对这个类进行一些测试,所以我会对这个类走一遍TDD的流程。
我遵循了TDD在UI层使用Espresso来实现新的功能,修改代码片段,修复bug。
更重要的是我使用TDD来实现它。
文中关于TDD的争论
没错,这个例子的确比较刻意也过于简单,正是如此我试图证明这一点。TDD是一个软件开发流程。也有人认为TDD代表别的什么。让我们来聊一聊相关的议题。
TDD不应该使用UI测试框架
我完全不同意这一点,因为TDD是软件开发流程。UI是软件的一部分,如果你想用TDD来开发它那么你就可以。步骤一表明我们需要编写测试。我们使用哪种框架对此有影响吗?没有。任何一点测试都比没有测试要好(但是这是另一个话题了)。看看我在维基百科上所找到的TDD资料,我发现在定义下的第一部分便是添加测试:
开发者可以使用任何测试框架编写测试来适应软件对应环境。
我们编写UI,因此Espresso作为UI测试框架是一个合适的测试框架。正确的工具。正确的事。
大多数人反对使用TDD来进行UI测试的主要原因是因为 它太慢,所以让我进入下一个话题。
当进行TDD时,所有的测试必需块
对此我有很深的体会。我的团队中有巨大的Espresso UI测试模块,它们运行完需要花费几个小时的时间。运行所有的测试是很痛苦且不可行的。因此我依靠CI来运行测试模块。在开发过程中,第二步和第四步我会运行一小部分测试。通常测试我正在编写的代码或者一小部分测试文件或包中国年相关的测试。这让我保持灵活。一般地这是一到二十个测试。
和JVM单元测试相比每秒可运行几百个,我同意它很慢。但是,我在这里做的UI开发所以考虑到UI测试的状况,我们很不幸的被这些慢速的测试所困。这就是现状,但是TDD流程依然有用。
在UI中做TDD是一个反模式
我多次看到过这种说法,主要是因为长时间运行测试是一个反模式,但是正如我前面所提及的,这就是解决的办法。你如何能绕过它?提取部分逻辑到MVP模式中。你可以在JVM测试中测试主要的代码。但你仍然需要一个小的UI测试层来保证你的UI像你想象的那样正常运行。
我这么说是因为你需要确认你的用户将会看到的内容…因为…
如果你的应用UI失败,崩溃,没有按照正常情况运行或者没有按照用户希望的那样运行,他们会认为这很垃圾。无论你的应用多么好看,如果UI没有按照希望的那样运行那么用户就会不高兴,而且不会喜欢使用你的应用。在这一个点上,你的工作将毫无意义。
多少次你使用一款应用时,起初觉得它看起不错,但是当你开始使用它的时候却往往不尽如人意。此时你的反应是什么?大多数都不太好。你可能不会使用它,甚至直接卸载它。我肯定不希望这样,我也坚信你也希望这样。
我想说的就是UI测试非常有用,即使是一点点,那一点也可以是通过TDD来驱动的测试。
但是,我听说TDD已经过时。
最近几年,这个说法一直持续不断。我已经了解了2003年由Kent Beck提出的TDD开始与重新定义,看到了它的兴起,衰落,东山再起等等。这是一个循环。有时它受人追捧,有时不是。最困难的地方是TDD很难。在一些语言中TDD被视为是帮助提高App总体设计的方式(通常是静态类型语言比如Java)。动态语言在测试时有其他更好的方式。
回到2014年,Martin Fowler, Kent Beck和DHH发布了一系列线上视频来讨论TDD是否已经过时了。你可以在这找到它们—Is TDD Dead?我会让你来定义TDD是否过时了。组中的每个人都有不错的观点,在那时我同意他们所表达的观点。
我不会在这争论TDD是否过时了。我会将它留给你定夺。
而然,我十分希望这篇文章可能证明TDD在UI开发中是可行的。
所以,TDD在UI开发中是否可行呢?
可行。
记住,TDD是软件开发流程,可以被应用到任何软件中。