使用测试驱动开发 (TDD) 进行软件交付
介绍
从某种程度上讲,我们可以从逻辑上看出我们所做的每件事都有先后顺序。如果我要建房子,我需要先打地基,然后砌墙,最后盖屋顶。我不能先盖屋顶,再打地基。
同样,在软件开发领域,您首先要收集需求,分析需求,设计(HLD 之后是 LLD),提出用户故事,编写一些代码,编写测试用例,执行测试,修改、重复,最后部署。打乱这些活动的顺序就像在打地基之前先建屋顶。然而,这正是我们在测试驱动开发中要做的。
什么是测试驱动开发?
测试驱动开发(以下简称 TDD)是一种迭代过程,在实施解决方案之前先编写测试用例。这种做法与先编码后测试的传统做法相反。
通过首先编写单元测试用例,开发人员可以设定一个可见的目标。单元测试用例旨在覆盖一小部分功能。一旦使用测试用例设定了“目标”,开发人员将能够编写足够的代码来使测试用例通过,而不会更多。
单元测试通过后,开发人员可以编写下一个单元测试用例,创建新的目标,并实现满足目标的解决方案。
因此,我们可以看到,首先设计测试有助于使整个开发过程更加顺畅和高效,因为它允许程序员尽早设定目标并编写足够多的代码来满足必要的要求。事实上,部署前的迭代次数越多,TDD 的优势就越大,因为单元测试被反复应用,确保新功能不会破坏旧功能。
因此,在这个迭代过程中,测试用例指导开发人员编写代码,这种方法因此得名:测试驱动开发。
TDD 的起源
TDD 是什么时候兴起的?这个问题很难回答,因为没有明确的答案。
通常,TDD 与敏捷项目管理相结合,因为敏捷也寻求以迭代方式工作,并相信使用更小的块进行构建。但编写代码之前编写测试用例的原则可以追溯到 1950 年代。
Kent Beck 因创建极限编程而备受赞誉,他是 TDD 方法的主要支持者,因此 TDD 得以复兴。他将 TDD 称为敏捷开发背景下的重新发现。
Beck 写道,TDD 的原理相当古老;它是在大型机时代采用的。开发人员遵循的做法是写下预期输出,然后在穿孔卡片上编写程序。这种方法很有效,因为他们玩系统的时间有限,这是最有效利用时间的方法。大型机上实现的输出与记录的输出进行比较,这将告诉开发人员他们的程序是否有效。
在重新发现阶段,Smalltalk 社区在其 SUnit 框架上重新引入了 TDD。当 Java 流行起来时,JUnit 框架是用于执行自动化单元测试的工具,而 Beck 是 JUnit 的开发人员之一。由于单元测试框架、敏捷革命和 Beck 之间的共同点,TDD 再次被引入主流。
TDD 流程
从高层次来看,图 1 提供了使用 TDD 开发软件的流程。
图 1:使用 TDD 开发软件的流程
步骤1:编写单元测试用例
正如文章前面提到的,TDD 中要做的第一件事就是编写单元测试用例。重申一下,单元测试用例必须尽可能小,但仍然能够独立测试。
假设我正在为应用程序开发登录页面。登录页面的标题部分有一个徽标,后面是用户名和密码字段。最后,有一个提交按钮来输入凭据。
在第 1 步中,我首先对将要放置在网页上的徽标进行单元测试。不多也不少。
第 2 步:执行测试
现在,我有一个单元测试用例,但没有任何代码。然而,我要执行该测试用例。为什么?在 TDD 中,你总是为失败的测试编写代码,这就是为什么我们明知没有代码支持它,却仍执行测试用例的原因。
执行不带代码的测试用例还可以确保开发人员不会因为需要在编写测试用例之前准备好代码而失去理智。这是一个额外的控制层,可确保遵循 TDD 的精神。
步骤 3:编写代码
测试失败后,您只需编写足够的代码即可成功执行测试用例。开发人员通常会编写大量代码,这更像是一本小说,而不是我们在 TDD 中寻找的要点。您不应该编写超过需要的代码。
步骤 4:执行测试
现在我们有了成功通过步骤 1 中创建的单元测试用例所需的代码,我们再次执行测试。如果测试失败,则表明开发的代码不足以运行测试用例。返回步骤 3 重新编写代码。
如果测试通过,则代码满足单元测试控制,现在可以继续执行步骤 5 进行重构。
步骤 5:重构代码
重构是提高代码效率的活动。您不会更改功能,而是改变内部动态以提高性能。一些示例包括识别和删除重复和无效的代码片段、简化嵌套类以及合并条件表达式。
通过步骤 4 中的单元测试的代码将经过重构的过程,从而提高代码的效率。
重构后,开发人员开始下一个单元测试用例,可能是他们正在开发的登录页面中的用户名字段。他们为用户名字段编写单元测试用例,并重复步骤 1 到 5。这个过程不断重复,直到功能或软件完全开发完成。
为什么要采用 TDD 方法?
经过此过程生成的代码是干净的。它没有错误,因为 TDD 方法测试指导并控制开发生命周期。它是对付缺陷的有效武器,否则缺陷就是困扰软件开发行业的主要问题。我并不是说我们要编写 100% 无缺陷的代码,但 TDD 的缺陷率远低于其他方法。
代码覆盖率是我们在软件开发中用来识别经过测试的代码百分比的一种度量。如果测试用例覆盖了代码的每一行,那么测量的代码覆盖率将为 100% - 这是理想情况下的目标。这是必要之恶,因为您不想引入任何尚未仔细检查的代码行。然而,许多组织坚持较低的数字,即 90-95% 之间的代码覆盖率。SonarQube 和 Atlassian Clover 等工具可帮助您进行代码覆盖率测量。
然而,使用 TDD,可以实现 100% 的代码覆盖率,因为您首先编写测试用例,并编写足够的代码以使测试用例通过。
TDD 有什么缺点吗?
在前面的部分中,我向您介绍了涉及 TDD 的过程。现在,让我们看看谁都参与了 TDD 方法论 - 如图 2 所示。
图 2:TDD 角色扮演
在标准的 TDD 项目中,业务分析师会从客户或产品所有者那里获取需求并将其传递给开发人员。开发人员根据对需求的理解,创建并执行单元测试用例并编写代码。测试执行成功后,开发人员将重构代码并针对分配给他的任务和用户故事重复 TDD 流程。
流程中的薄弱环节在于开发人员对需求的理解。假设业务分析师解释说客户的需求是开发一个帐户管理系统,开发人员可能会误解需求并开发出完全不同的东西,例如客户关系管理系统。
正如您在角色扮演图中看到的,TDD 中没有测试人员参与。开发人员编写测试用例并开发代码。因此,与测试人员参与流程相比,整个周期中对需求的验证较少。
偏离需求是一个代价高昂的错误,通常会导致花费数小时重新解决问题。这会浪费金钱、延迟项目时间表,并且经常激怒客户。在我看来,这是 TDD 方法的主要缺点:TDD 更容易在理解需求时犯错误,并在这方面不受控制地继续编码过程。
还有其他所谓的缺点,例如这个过程需要一个人付出太多的努力和时间。然而,在这方面,我认为首先编写测试用例并实现干净的代码和代码覆盖率是有价值的。在我看来,拥有准确的代码和专注的开发值得花费额外的精力和时间。
此外,有人说单元测试似乎专注于单个类和函数,而不是整体用户故事和客户需求。我同意单元测试用例在微观层面上工作,可能无法全面评估需求。这个问题可以通过我将在下一节介绍的方法来解决。
TDD 的替代方案
我在上一节中讨论的缺点通常可以通过 TDD 的升级版本来解决。标准 TDD 最突出的升级是 ATDD 和 BDD。
糖尿病性发展障碍
ATDD,即验收测试驱动开发,与 TDD 相比,它有几个重大改进。首先,验收测试用例是在编写用户故事时编写的,而不是编写单元测试用例,然后开发代码。其次,测试用例由测试人员编写,代码由程序员开发——这增加了验证代码是否符合业务分析师定义的客户期望。
然而,即使是 ATDD 也有一些缺点。如果测试人员和程序员都对需求理解不正确怎么办?我们可能需要另一层控制和验证。这一层由行为驱动开发 (BDD) 方法提供。
身体缺陷诊断
在 BDD 中,会开发验收测试用例,但需求获取方式有显著的差异。业务分析师需要提供需求以及每个需求的相关示例。通过使用这些示例,测试人员和程序员可以更准确地构建需求。
考虑 BDD 时,下一个主要区别是 BDD 测试用例不是用通常的脚本语言编写的。而是采用自然语言形式,例如 Gherkin。使用自然语言编写验收测试用例将使业务分析师、产品所有者和客户能够根据需求进行验证。这有助于避免代码与其输出之间的任何差异,但会增加开发过程的复杂性,因为代码现在需要满足 Gherkin 要求。
支持 BDD 方法的工具。
如今,大多数采用 TDD 的项目都在转向 BDD。BDD 之所以具有吸引力,是因为它可以轻松准备验收测试用例,并且它是开发团队和客户之间的天然桥梁。两者之间的接近性增强了沟通交流,减少了黑匣子带来的摩擦和延迟。
尽管 BDD 如今已独立存在,但它的根源在于 TDD 方法。数十年前发明的 TDD 原则通过开发方法的变体继续推动着行业的发展。
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~