连续交付在实践中如何工作?


22

连续交付听起来不错,但是我多年的软件开发经验表明,在实践中它是行不通的。

(编辑:为了明确起见,我总是有很多测试会自动运行。我的问题是关于如何使每次签入都充满信心,我知道这是CD的完整格式。替代方法不是一年周期每周两次(如果正确完成,有些人可能会认为它仍然是CD),两周或一个月的迭代;在每个迭代的末尾都包含老式的质量检查,以补充自动化测试。

  • 无法全面覆盖测试。对于每件事,您都必须投入大量时间-时间就是金钱。这是有价值的,但是可以花费时间以其他方式为质量做出贡献。
  • 有些事情很难自动测试。例如GUI。甚至Selenium也不会告诉您您的GUI是否不稳定。没有笨拙的固定装置,很难测试数据库访问,甚至无法覆盖数据存储中的奇怪情况。同样的安全性和许多其他事情。只有业务层代码才可以有效地进行单元测试。
  • 即使在业务层中,大多数代码也不是简单函数,其参数和返回值可以很容易地隔离以进行测试。您可能会花费大量时间来构建模拟对象,这可能与实际实现不符。
  • 集成/功能测试是对单元测试的补充,但是它们需要大量时间才能运行,因为它们通常涉及在每个测试中重新初始化整个系统。(如果不重新初始化,则测试环境会不一致。)
  • 重构或任何其他更改都会破坏很多测试。您花费大量时间修复它们。如果只需要验证有意义的规范更改,那很好,但是测试常常由于无意义的低级实现细节而失败,而不是真正提供重要信息的东西。通常,调整主要集中在重新测试内部,而不是真正地检查正在测试的功能。
  • 错误的现场报告无法轻松地与代码的精确微版本相匹配。


4
连续交付不需要测试(但有帮助)。它指的是您可以根据需要定期将您构建的东西运送给客户的事实。

有趣的是,您对连续交付的异议绝大多数都将测试作为CD的要素。但是,这只是难题的一部分:您需要称职的内部工具,开发人员致力于严格的质量检查,自动化测试中采用广度优先的方法,更不用说强大的组织支持了。可以做到,但是需要很多人致力于这一事业。
斯蒂芬·格罗斯

1
@Stephen是的,我专注于测试,因为我在所有其他方面都表示同意。我也赞成测试。我只是看不到您如何有足够的信心来部署每个签入。
约书亚·福克斯

@ThorbjørnRavn Andersen。当然,CD似乎需要测试。您如何有信心自动发送没有它的每个签到。
约书亚·福克斯

Answers:


29

我多年的软件开发经验表明,实际上它是行不通的。

你试过了吗?我和Dave都是根据我们自己以及ThoughtWorks的其他资深人士多年的集体经验写的书,实际上是在做我们讨论的事情。书中没有任何东西是推测性的。我们讨论的所有内容甚至在大型分布式项目中都经过了尝试和测试。但是我们不建议您出于信仰。当然,您应该自己尝试一下,并写下您发现有用的内容和不起作用的内容(包括相关的上下文),以便其他人可以从您的经验中学习。

持续交付非常注重自动化测试。我们花了大约1/3的书来谈论它。我们这样做是因为替代方法-手动测试-昂贵且容易出错,而且实际上并不是构建高质量软件的好方法(如Deming所说:“停止对大规模检查的依赖以实现质量。改进过程并将质量纳入首先的产品”)

无法全面覆盖测试。对于每件事,您都必须投入大量时间-时间就是金钱。这是有价值的,但是可以花费时间以其他方式为质量做出贡献。

当然,完全的测试范围是不可能的,但是还有什么选择:零测试范围?需要权衡。介于两者之间的位置是您项目的正确答案。我们发现,一般来说,您应该花费大约50%的时间来创建或维护自动化测试。在您考虑进行全面的手动测试以及修复向用户发布的错误的成本之前,这听起来可能很昂贵。

有些事情很难自动测试。例如GUI。甚至Selenium也不会告诉您您的GUI是否不稳定。

当然。查看Brian Marick的测试象限。您仍然需要手动执行探索性测试和可用性测试。但这就是您应该将昂贵且有价值的人用于-而不是回归测试。关键是您需要放置一个部署管道,这样您才可以对已经通过了全面的自动化测试套件的内部版本进行昂贵的手动验证。因此,你既减少的钱,你对手动测试花费的金额,并不断使其手动测试或生产缺陷的数量(到那时他们是要修复昂贵)。在产品的整个生命周期中,正确完成自动测试的成本低得多,但是随着时间的推移,这笔资本支出会摊销自己。

没有笨拙的固定装置,很难测试数据库访问,甚至无法覆盖数据存储中的奇怪情况。同样的安全性和许多其他事情。只有业务层代码才可以有效地进行单元测试。

通过基于端到端方案的功能接受测试对数据库访问进行隐式测试。安全将需要自动和手动测试的结合-自动渗透测试和静态分析,以发现(例如)缓冲区溢出。

即使在业务层中,大多数代码也不是简单函数,其参数和返回值可以很容易地隔离以进行测试。您可能会花费大量时间来构建模拟对象,这可能与实际实现不符。

当然,如果您构建软件且测试很差,则自动化测试会很昂贵。我强烈建议您阅读“在测试的指导下不断发展的面向对象软件”这本书,以了解正确的做法,以便随着时间的推移可以维护您的测试和代码。

集成/功能测试是对单元测试的补充,但是它们需要大量时间才能运行,因为它们通常涉及在每个测试中重新初始化整个系统。(如果不重新初始化,则测试环境会不一致。)

我曾经使用的一种产品具有一套3500个端到端验收测试套件,需要运行18小时。我们在70个盒子的网格上并行运行它,并在45m内获得反馈。确实还比理想的还要长,这就是为什么我们在几分钟内运行完单元测试后,将其作为管道的第二阶段运行,因此我们不会在没有基本水平的构建上浪费我们的资源。信心。

重构或任何其他更改都会破坏很多测试。您花费大量时间修复它们。如果只需要验证有意义的规范更改,那很好,但是测试常常由于无意义的低级实现细节而失败,而不是真正提供重要信息的东西。通常,调整主要集中在重新测试内部,而不是真正地检查正在测试的功能。

如果您的代码和测试被很好地封装并且松散耦合,则重构不会破坏很多测试。我们在书中还描述了如何对功能测试执行相同的操作。如果您的验收测试失败,则表明您缺少一个或多个单元测试,因此CD的一部分涉及不断提高您的测试覆盖率,以尝试在交付过程中尽早发现错误,使测试的粒度更细,错误更便宜。

错误的现场报告无法轻松地与代码的精确微版本相匹配。

如果你的测试和更频繁的释放(CD点的一部分),那么它相对简单的,以确定导致错误的改变。CD的全部目的是优化反馈周期,以便您可以在检入版本控制后尽快发现错误-实际上,最好在检入之前(这就是我们运行构建和单元测试的原因)签到之前)。


感谢您的回答。是的,我相信测试。我的项目在日常构建中运行的自动化测试中具有良好的代码覆盖率。我只是说在发布之前需要某种迭代。“您仍然需要手动进行探索性测试。” 我不明白 完整的CD系统将部署在每个签入中。如果包括手动测试,该怎么办?
约书亚·福克斯

3
我喜欢区分持续交付和持续部署。这是区别。连续交付意味着您可以始终保持系统准备就绪,只需按一下按钮即可按需发布。发布是业务决策。连续部署是一个限制情况,您需要释放每个良好的构建(请注意,并非每个签入-某些签入不会导致可释放的构建)。在这两种情况下,您都可以包括手动验证:关键是部署管道的概念。
Jez Humble,

关于“数据库访问已通过基于端到端方案的功能接受测试隐式测试。” 关键问题是这是隐式的。人们似乎对此感到满意,但这是一种非常浪费时间的方法。而不是告诉问题“这是我期望数据库获得的,而是得到了它”,而是说“在100层之一中发生了故障”。
atoth,2016年

11

首先,CD需要进行重大的心理调整-您必须承认有时,无论您做什么,事情都会变得很糟糕。归根结底,您不能证明负面。

一旦克服了这一点,您就会意识到您需要工具和过程来a)很快地捕获这些错误,b)非常高效地回滚或部署更新。而且,如果您真的在喝CD鸡尾酒,那么您实际上是在交付许多小而有针对性的更改,这些更改很容易回滚,并且不应该引入主要的应用程序范围的错误。甚至那些真正从事CD工作的人都以更传统的方式处理重大的转换。


“小的...更改...不应该引入应用程序范围的错误”。即使在结构合理的代码中,也会发生这种情况。例如,您添加了一个div,该div在一个特定的浏览器中将另一个div推出了视图。例如,在意外的极端情况下会出现null值,从而引发异常并完全阻止GUI呈现。是的,您应该像我一样测试所有可能的东西,但是不可避免地会发生错误,并且小错误可能会破坏整个应用程序。
约书亚·福克斯

但是仍然很容易找到和修复它们,这是重点。
Wyatt Barnett

2

每个系统都有风险,每个风险都有潜在的成本。如果某种微小的风险(可能需要花费数月或数年才能在广泛的测试和质量保证中找到)的成本足够高(您的心脏起搏器中的软件),那么您就不会在没有进行大量测试的情况下交付产品冻结释放。如果失败的代价足够小,那么也许您会连续进行零测试(您猫的Facebook页面)。


是。对于大多数生产应用程序,风险介于两者之间。在我看来,这样的风险使得即使在进行自动测试的情况下,我们也不想在每次签入时都进行部署。似乎总是需要进行一次质量检查。但是大约每周发布一次对我来说似乎可行。
约书亚·福克斯

1

无法全面覆盖测试。对于每件事,您都必须投入大量时间-时间就是金钱。这是有价值的,但是可以花费时间以其他方式为质量做出贡献。

您不需要100%的覆盖率,也需要足够的信心才能对系统充满信心,只需更改一个位置就不会破坏您以前已经证明的工作。

有些事情很难自动测试。例如GUI。甚至Selenium也不会
告诉您您的GUI是否不稳定。没有笨重的固定装置,很难测试数据库访问,甚至无法覆盖数据存储中的奇怪情况。

但是,数据库访问很容易编写。您不需要在数据层上进行很多测试,因为它只是获取和设置数据。最重要的是确保它失败时回滚并记录问题,以便您可以解决它。

同样的安全性和许多其他事情。只有业务层代码才可以有效地进行单元测试。即使在业务层中,大多数代码也不是简单函数,其参数和返回值可以很容易地隔离以进行测试。

然后,您的许多函数/类都太大了。它们应被编写为可测试的。

您可能会花费大量时间来构建模拟对象,这可能与实际实现不符。

模拟对象的I / O应该与预期的相匹配。它内部发生什么无关紧要。

集成/功能测试是对单元测试的补充,但是它们需要大量时间才能运行,因为它们通常涉及在每个测试中重新初始化整个系统。(如果不重新初始化,则测试环境会不一致。)

这些不应该一直运行。根据需要。

重构或任何其他更改都会破坏很多测试。您花费大量时间修复它们。如果只需要验证有意义的规范更改,那很好,但是测试常常由于无意义的低级实现细节而失败,而不是真正提供重要信息的东西。通常,调整主要集中在重新测试内部,而不是真正地检查正在测试的功能。

然后,您的代码过于紧密地耦合在一起,并且您的测试编写得很差。

错误的现场报告无法轻松地与代码的精确微版本相匹配。

不知道您在这里得到什么?如果存在错误,请编写测试以表明其存在,然后对其进行修复。


“模拟对象的I / O应该符合预期。”构建完全实现接口规范的MO很耗时。更糟糕的是,必须不断更新它们,以便一个人有效地编写所有代码两次(一次生产和一次MOS)。
约书亚福克斯

1

对我们来说效果很好,但我们的客户主要来自内部。白天完成了多次构建,不容忍损坏的构建,使用了Web启动机制,因此用户每次启动时都会获得最新版本。一件事是CD使许多问题消失了。是的,您始终会遇到兼容性问题,但是,由于每次都只部署小的更改,因此解决问题确实很容易。CD的压力级别比我们进行大的更新时要低得多,并且不得不希望一切都正确,因为在发生故障的情况下有太多的代码需要检查。


-4

老实说,所有软件都在不断交付中!最重要的是将其发布!让您的用户使用它,并优先处理功能请求和挤压错误。

“真正的艺术家之船”
-史蒂夫·乔布斯。


CD的替代方法不是长达一年的周期。它是每周,两周或一个月的迭代。
约书亚·福克斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.