被高估了的测试驱动开发?

测试驱动开发(TDD)始于上世纪 90 年代,时至今时今日,依然只有少数的开发者在践行着。本文作者从软件开发者的角度,又一次帮助我们定义了测试驱动开发,解答了众多开发着对 TDD 常见的谬误。

作者 | Tylor Borgeson,已获作者翻译授权
译者 | 罗昭成
原文 | “Test Driven Development is overrated”
本文首发于 CSDN 微信(ID:CSDNnews)

以下为译文:

这是我「流行软件开发实践」系列文章中的第二部分,在本系列文章中,我计划包含软件工程师通过提升开发流程和实践来改善软件开发的一系列方法。我曾在 ThoughtWorks 担任软件顾问,现在我在德国一家大型的零售公司工作,这些方法都是我在职业生涯中学习并实践验证过的。

我曾和一位客户的开发人员讨论过有关软件开发方面的问题。我们的讨论有点过头,他提到,作为一个软件开发者非常地幸运,因为“我们可以欺骗公司为我们的基础工作付出可观的收入”……我认为,不论你的编程水平如何,你也不能如此贬低众多软件开发工程师。

我们聊得如丝般顺滑,讨论逐渐深入到敏捷软件开发实践上。对于敏捷开发中提到的方法,他们表示可以进行尝试,用于改进当前的工作方式,但是,当我提到测试驱动开发的时候,他们却都觉得:

测试驱动开发名不副实。

听到他们的这个评论,我感到非常震惊。同时,他们也让我意识到,在很多人心中,测试驱动开发仅仅是敏捷开发实践中的一种方式,在更多人心里,它更像是海市蜃楼,看起来很美,用起来却不过如此。为了让更多的人了解测试驱动开发,我想用本篇文章告诉大家到底什么是测试驱动开发。

首先,我来定义一下什么是测试驱动开发(TDD)。

顾名思义,TDD 是一种软件开发的策略,它通过写测试来引导开发流程。Kent Beck 2003 年在他的《测试驱动开发》一书中提到了这个概念(译者注:Kent Beck,美国著名软件工程师与作家,在软件工程方面有很大贡献。他是 Smalltalk 软件的开发者,设计模式的先驱,测试驱动开发的支持者,也是极限编程的创始者之一。),实现测试驱动开发主要遵循以下三个步骤:

  • 为你要写的一小部分功能编写一个失败的测试用例。

  • 实现你的功能逻辑,让你的测试用例通过。

  • 重构代码,保证代码的结构与可读性。

这个流程可以简化为“红色,绿色,重构”。

下面,我将用测试驱动开发的模式,用 Python 实现勾股定理中斜边值的计算。


△先写测试代码,方法的实现先不用管


△运行测试代码,出现红色的错误


△实现代码逻辑,让测试代码通过


△重构代码,使代码逻辑更优雅

今年已经是 2020 年了,17 年过去了,对于很多开发者而言,测试驱动开发带来的收益依然没有定论,因此,大多数开发人员都不曾使用过 TDD 。

一直以来,我都热衷于向我的朋友们推荐使用 TDD ,但依然有很多人选择拒绝,当我问到为什么时,以下是我听到的最多的几个回答:

我们有测试团队,写测试是他们的工作。

在写测试用例的时候,需要 mock 上下文对象,所以需要花费更多的时间和精力。

并不能给我带来很大的收益。

它太慢了。

下面,我来分析一下这几个回答。

场景 1:“我们有测试团队,写测试是他们的工作”

我一直觉得,谁写的代码,谁就应该要保证它能正常运行,所以我每次想到这个答案我都会觉得好笑。如果你觉得只有写逻辑代码才是你的工作,那么你的能力将会停滞不前。

每一个程序员写的代码应该都要具备以下几个性质:

  • 根据业务需求选择使用正确的技术栈

  • 代码容易理解

  • 可测试

  • 可扩展

  • 简单

我认为,把开发者能做的单元测试交给测试部门来做,是非常不明智的行为。测试部门有更多更重要的事情要做,而不是浪费时间来做黑盒测试。

场景 2:“在写测试用例的时候,需要 mock 一些上下文对象”

你们的想法和无奈感,我都能理解,曾经的我也和你们一样。在你的面前有一个方法,这个方法有三个不同参数,并且每一个参数都是一个对象,同时,它们的属性也不为空。现在你需要对这个方法进行测试,要满足测试条件,需要进行大量的 Mock,这会导致为了测试一个方法而写大量的额外代码。

现在,我已经能够很好的处理这个问题,并且我认为做这些事情都是值得的。

每当我思考如何降低写测试的成本时,脑海里就会萦绕着这两样东西,分别是SOLID(译者注:S 指单一职责原则,O 开闭原则,L 里氏替换原则,I 接口隔离原则,D 依赖倒置原则)和测试金字塔。

需要强调的是,S —— 单一职责原则,这个原则告诉我们,一个类或者一个方法都只能做一件事情,这样可以防止它们对系统的其他部分产生副作用。

我知道,公司中的代码逻辑肯定不会像两个整数相加那么简单,但是,如果我们可以让这些方法的职责变得单一,那么写测试也就会变得非常容易。

△测试金字塔,来源:martinfowler

这个测试金字塔通常用来衡量不同测试类型的优先级。

UI 测试和服务测试会耗费大量的时间,成本很高,单元测试却可以毫秒级地运行,所以一个系统应该让它具有更多的单元测试,少一点服务测试和 UI 测试。这样做和 DevOps 的目标一致,可以增加整个系统开发过程中的反馈循环。

单元测试要求新的代码与系统中原有的代码尽可能少的耦合在一起,这样能更容易编写单元测试的代码。

测试驱动开发是基于单元测试的一种开发模式。

当然,说起来比做起来容易得多。对于各种类型测试来说,都会有成本,单元测试也不例外。如果有一天,你发现你写单元测试的成本增加了很多,你可以扪心自问,是否有以下两个问题:

  • 当前的测试是否在单元测试范围内,是否需要把升级到服务测试的范围?

  • 你的代码结构是否合理,你的类和方法是否单一职责?

如果你在编写单元测试代码时,都需要不断的投入大量成本,这就意味着,你的代码存在一些问题,你需要对其进行一些重构,让代码结构更清晰。

场景 3:“并不能给我带来很大的收益”

很搞笑的是,大多数这么说的人,都是没有真正使用测试驱动开发模式的人。纸上得来终觉浅,绝知此事要躬行。

下面,我将介绍使用 TDD 带来的两大好处:

第一个比较明显,根据 TDD 的定义,在编写真实的逻辑代码之前,都需要写先测试,以这种方式产出的代码都是自测试的。正如 Martin Fowler(译者注:Martin Fowler,英国著名软件工程师,也是一个软件开发方面的著作者和国际知名演说家,专注于面向对象分析与设计,统一建模语言,领域建模,以及敏捷软件开发方法,包括极限编程。)所说:

如果你的代码是自测试的,那么只要你针对代码运行一系列自动化测试,等到通过后,你就可以保证你的代码不会出现任何实质的缺陷。

换句话说,你的代码可以按照你在测试代码中定义的流程正常运行。

单元测试非常的有用,它能让你对你的代码充满信心。在重构的时候,它能在第一时间发现重构造成的问题,并且,并且单元测试也可以让你知道当前系统中是否存在 Bug 。

自测试的代码可以让整个团队在开发和集成的过程中受益。

使用 TDD 另一个好处是,它能让开发人员在编写逻辑代码之前,仔细思考要编写什么样的代码,以及如何去编写代码。在编码之前的思考,能够让开发者真正深入业务需求,考虑可能存在的边界问题和可能存在的挑战,这个过程虽然费时而且很痛苦,但它并不是浪费开发人员的精力。不仅如此,先写测试,还会促使开发人员从一开始写代码的时候,就考虑系统的设计与架构,这样可以大大地提高系统扩展性。

场景 4:“它太慢了”

以我的经验来看,有这种想法的人,大多都是那些尝试使用 TDD 的人,然而他们去使用的是以前开发模式,先写代码,最后在来编写测试。这也是大多数开发人员不能坚持执行 TDD 最常见的原因之一。

我深深地理解这些开发者的想法,在编写代码之前,先进行思考,然后花时间去写测试代码,比起不写测试,确实要花费更多的时间。有时候,模拟 mock 出场景有效的数据不仅比较麻烦,也会让人感到很沮丧。

虽然我说很多 TDD 的优点,但是这无济于事,很多公司的业务部门都将开发交付的时间作为衡量标准,整个团队都在拼尽全力开发新的需求。

在敏捷开发中,衡量软件开发有四个关键指标(我经常会参考这些指标),其中两个为变更失败率和部署频率。正如我上面所说,假设系统使用 TDD 构建,当你提交代码,整个团队都会知道代码的修改是否会导致系统其它功能崩溃,同时还能提高 Bug 被发现的速度。单元测试的局部性,可以让开发人员可以快速的找到问题出现的原因。如此往复,导致功能出现异常的可能性会越来越小,从而降低变更失败率。

代码通过单元测试,也可以让我们对软件功能的鲁棒性有更强的信心,整个团队都可以通过单元测试结果来决定是否可以部署,任何人都可以按需进行部署。

《凤凰项目》中列出了四种工作类型,其中一种是“计划外工作或救火工作”。这类工作通常是由于错误引起的,开发者必须停止手上的工作去优先处理。自测试的代码可以最大的程度上减少这种错误,这就意味着,它能帮助我们最小化“计划外工作或救火工作”,还能最大限度的提高开发人员的满意度。快乐的开发就是好的开发。

当开发者不用担心线上会出现问题的时候,他们能够更加聚焦在新功能的开发上。他们在写代码之前,有考虑过如何写代码,便能够写出符合 SOLID 原则的代码。在开发中会不断地对代码进行重构,技术债也会降到最低。团队对代码质量充满信心,能随时进行上线部署,这会使整个团队跑得更快。

万事开头难,和其他工具和实践一样,测试驱动开发在刚开始的时候,可能会让人很难受,并且会让你的开发效率降低,但是,正如持续交付之父 Jez Humble 所说:

提前并频繁地做让你感到痛苦的事。

现在去开始你的测试驱动开发之旅,直到它让你觉得舒服为止。谢谢你的阅读。

引用:

系列阅读

1. 为什么持续集成和部署在开发中非常重要?

2. 被高估了的测试驱动开发?

3. 为什么程序员如此“嫌弃”主干开发模式?

4. 程序员为什么千万不要瞎努力?

5. 为什么许多程序员讨厌结对编程?

6. 程序员如何用代码彻底终结系统那些事儿?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
测试驱动编程是 XP 困扰程序员的一个方面。对于测试驱动编程意味着什么以及如何去做,大多数人都做出了不正确的假设。这个月,XP 方面的师兼 Java 开发人员 Roy Miller 谈论了测试驱动编程是什么,它为什么可以使程序员的生产力和质量发生巨大变化,以及编写测试的原理。请在与本文相随的 论坛中提出您就本文的想法,以飨笔者和其他读者。(您也可以单击本文顶部或底部的“讨论”来访问该论坛。) 最近 50 年来,测试一直被视为项目结束时要做的事。当然,可以在项目进行之中结合测试测试通常并不是在 所有编码工作结束后才开始,而是一般在稍后阶段进行测试。然而,XP 的提倡者建议完全逆转这个模型。作为一名程序员,应该在编写代码 之前编写测试,然后只编写足以让测试通过的代码即可。这样做将有助于使您的系统尽可能的简单。 先编写测试 XP 涉及两种测试: 程序员测试和 客户测试测试驱动编程(也称为 测试为先编程)最常指第一种测试,至少我使用这个术语时是这样。测试驱动编程是让 程序员测试(即单元测试 ― 重申一下,只是换用一个术语)决定您所编写的代码。这意味着您必须在编写代码之前进行测试测试指出您 需要编写的代码,从而也 决定了您要编写的代码。您只需编写足够通过测试的代码即可 ― 不用多,也不用少。XP 规则很简单:如果不进行程序员测试,则您不知道要编写什么代码,所以您不会去编写任何代码。 测试驱动开发(TDD)是极限编程的重要特点,它以不断的测试推动代码的开发,既简化了代码,又保证了软件质量。本文从开发人员使用的角度,介绍了 TDD 优势、原理、过程、原则、测试技术、Tips 等方面。 背景 一个高效的软件开发过程对软件开发人员来说是至关重要的,决定着开发是痛苦的挣扎,还是不断进步的喜悦。国人对软件蓝领的不屑,对繁琐冗长的传统开发过程的不耐,使大多数开发人员无所适从。最近兴起的一些软件开发过程相关的技术,提供一些比较高效、实用的软件过程开发方法。其中比较基础、关键的一个技术就是测试驱动开发(Test-Driven Development)。虽然TDD光大于极限编程,但测试驱动开发完全可以单独应用。下面就从开发人员使用的角度进行介绍,使开发人员用最少的代价尽快理解、掌握、应用这种技术。下面分优势,原理,过程,原则,测试技术,Tips等方面进行讨论。 1. 优势 TDD的基本思路就是通过测试来推动整个开发的进行。而测试驱动开发技术并不只是单纯的测试工作。
Python作为一门编程语言,确实在近年来受到了广泛的关注和使用。它具有简洁易读的语法,丰富的库和框架支持以及广泛的社区支持,使得编写Python代码变得高效且快速。然而,是否被严重高估这个问题需要从多个角度来看待。 从技术层面来看,Python并不是解决所有问题的最佳选择。与其他编程语言相比,Python的执行速度较慢,特别是在处理大规模数据和高性能计算方面。因此,在对速度要求较高的领域,如游戏开发、金融分析等,Python可能不适用。 此外,Python的高估也与其在人工智能和数据科学领域的广泛应用有关。因为Python有许多强大的机器学习和数据处理库,例如TensorFlow、Pandas和NumPy等,这使得Python成为了人工智能和数据科学领域的首选语言。然而,这也导致了一种过度使用Python的现象,有些人可能过于依赖它,而忽视了其他更适合的工具和语言。 此外,Python在某些领域可能被高估的另一个原因是其相对简单的语法使得它成为了很多初学者的首选语言。然而,要成为一名优秀的程序员,除了掌握Python之外,还需要了解其他编程语言和技术。过分关注Python可能导致一种技术上的狭隘性。 总体来说,尽管Python有很多优点,但它并不是解决所有问题的最佳选择。因此,我们应该避免过分高估Python的能力,而是在不同的场景下选择最适合的工具和语言。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值