如何采用TDD并确保遵守?

2022-09-02 12:01:13

我是一名高级工程师,在另外四人的团队中工作,开发一个本土内容管理应用程序,该应用程序驱动着一个大型美国职业体育网站。大约两年前,我们开始了这个项目,并选择Java作为我们的平台,尽管我的问题不是特定于Java的。自从我们开始以来,我们的队伍中出现了一些变动。我们每个人在决定执行细节方面都有很大程度的自由度,尽管重要的决定是通过协商一致方式作出的。

我们的项目是一个相对年轻的项目,但是我们已经处于没有一个开发人员对该应用程序一无所知的地步。造成这种情况的主要原因是我们发展速度快,其中大部分发生在我们这项运动赛季揭幕战的紧要关头。事实上,我们的测试覆盖率基本上是0。

我们都了解TDD的理论优势,并原则上同意,如果我们多年来一直坚持使用这种方法,这种方法将改善我们的生活和代码质量。这从未站稳脚跟,现在我们负责一个未经测试的代码库,它仍然需要大量的扩展,并在生产中积极使用,并被公司结构所依赖。

面对这种情况,我只看到两种可能的解决方案:(1)追溯性地为现有代码编写测试,或者(2)尽可能多地重写应用程序,同时狂热地遵守TDD原则。我认为(1)基本上是不切实际的,因为我们在项目中有一个地狱般的依赖关系图。我们的组件几乎都不能单独测试。我们不知道所有的用例;在测试推送期间,由于业务需求或作为对不可预见问题的反应,用例可能会发生变化。由于这些原因,我们无法真正确定一旦完成测试,我们的测试将会变成高质量的。存在将团队带入虚假安全感的风险,即微妙的错误会在没有人注意到的情况下悄悄潜入。鉴于投资回报率的前景黯淡,我自己或我们的团队很难向管理层证明这种努力是合理的。

方法(2)更具吸引力,因为我们将遵循测试优先原则,从而生成几乎100%覆盖的代码。即使最初的努力最初导致覆盖代码孤岛,这也将为我们在实现项目范围的覆盖过程中提供一个重要的滩头阵地,并有助于解耦和隔离各种组件。

这两种情况下的缺点是,我们团队的业务生产力可能会显着减慢,或者在任何测试推送期间完全蒸发。在业务驱动的危机期间,我们负担不起这样做,尽管随后是相对平静的,我们可以利用它来达到我们的目的。

除了选择正确的方法((1),(2)或其他未知的解决方案)之外,我还需要帮助回答以下问题:从长远来看,我的团队如何确保我们的努力不会因未维护的测试和/或随着业务需求的不断增长而无法编写新测试而浪费?我在这里对各种各样的建议持开放态度,无论它们涉及胡萝卜还是大棒。

无论如何,感谢您阅读有关这种自我造成的困境的信息。


答案 1

“这两种情况下的缺点是,我们团队的业务生产力可能会显着放缓,或者在任何测试推送期间完全蒸发。

这是对事实的常见误解。现在你有你不喜欢的代码,并且很难维护。“地狱般的依赖关系图”等。

因此,您一直在做的“关键”开发导致了昂贵的返工。返工如此昂贵,你不敢尝试。这说明你的快速发展不是很有效。这在当时看起来很便宜,但回想起来,你注意到你真的在浪费开发资金,因为你创造了有问题的、昂贵的软件,而不是创造好的软件。

TDD可以改变这一点,这样您就不会生产维护成本高昂的紧缩软件。它不能解决所有问题,但它可以清楚地表明,将注意力从“紧缩”中改变过来可以产生更好的软件,从长远来看,这些软件的成本更低。

从您的描述来看,您当前代码库的某些(或全部)是负债,而不是资产。现在想想TDD(或任何学科)将采取什么措施来降低负债的成本。“生产力”的问题不适用于您产生负债的情况。

TDD的黄金法则:如果您停止创建负债的代码,则组织将获得积极的投资回报率。

询问如何保持当前的生产力速度要小心。其中一些“生产力”正在产生没有价值的成本。

“我们的组件几乎都不能单独测试;我们不知道所有的用例”

正确。将单元测试改装到现有的代码库中是非常困难的。

“存在将团队带入虚假安全感的风险,即细微的错误会在没有人注意到的情况下悄悄潜入”

假。没有“虚假的安全感”。每个人都知道测试充其量是岩石。

此外,现在你有可怕的错误。你有问题,你甚至不知道它们是什么,因为你没有测试覆盖率。

交易一些微妙的错误仍然是一个巨大的改进,而不是你无法测试的代码。我每天都会对未知的错误进行细微的错误。

“方法(2)更有吸引力”

是的。但。

您之前的测试工作被一种奖励紧缩编程的文化所颠覆。

有什么变化吗?我怀疑。您的文化仍然奖励紧缩编程。您的测试计划可能仍会被颠覆。

你应该看看中间地带。不能指望你在一夜之间“狂热地坚持TDD原则”。这需要时间,需要重大的文化变革。

您需要做的是将应用程序分解为多个部分。

例如,考虑模型 - 服务 - 视图层。

您有核心应用程序模型(持久性事物,核心类等),需要广泛,严格的可信赖测试。

您的应用程序服务需要一些测试,但“由于业务需求或对不可预见问题的反应,用例可能会在测试推送期间发生变化”。尽可能多地测试,但不要与在下个赛季按时发货的必要性相冲突。

你有视图/演示的东西需要一些测试,但不是核心处理。这只是演示。随着人们想要不同的选项,视图,报告,分析,RIA,GUI,浮华和嘶嘶声,它将不断变化。


答案 2

在回答以下问题时,我需要帮助:从长远来看,我的团队如何确保我们的工作不会因未经维护的测试和/或随着业务需求的不断增长而无法编写新测试而浪费?

确保生成过程在每次生成时执行测试,并在失败时使生成失败。

是否使用持续集成?哈德森是一个很好的工具。它可以为项目生命周期内的每个构建保留测试次数、失败次数、测试覆盖率等的图表。这将帮助您轻松关注您的覆盖率何时下降。

正如你所提到的,将单元测试改造到现有项目中可能非常困难,更不用说TDD了。我祝你好运!

更新:我还想指出,100%的测试覆盖率并不是一个很好的目标,当你试图从~80%或~90%时,它的回报会递减。要获得最后几个百分点,您需要开始模拟代码中每个可能的分支。你的团队将开始花时间模拟在现实生活中无法发生的场景(“当我关闭它时,这个流实际上不会抛出IOException,但我需要覆盖这个分支!”),或者在你的测试中没有真正的价值。我发现我的团队中有人在验证,因为方法的第一行实际上在值为 .if (foo == null) throw new NullPointerException(...);null

最好花时间测试真正重要的代码,而不是强迫性地让最后一行在Emma或Cobertura中显示为绿色。


推荐