Sam Newman 对端到端测试的看法
咨询顾问和作者Sam Newman同样广泛讨论了反馈周期在测试中的重要性。他认为,在需要快速迭代的敏捷开发环境中,测试的反馈时间应尽可能短。这对于使开发人员能够立即识别并修复问题,而无需等待长时间的测试结果至关重要。
对于Newman来说,长反馈周期(如端到端测试通常伴随的)可能对开发过程产生不利影响。当开发人员不得不长时间等待更改是否成功的反馈时,团队的生产力和士气都会受到影响。此外,长反馈周期可能导致在发现问题之前更改了大量代码,这使得调试和错误修复更加复杂。
Newman提倡使用“测试金字塔”,其中基础由单元测试组成,提供快速反馈。单元测试之上是集成测试,验证服务内部组件之间的交互。在金字塔的顶部是端到端测试,它们较慢且执行成本较高,但在验证系统关键流程中仍有其作用。这个结构允许大多数反馈快速获得,保持开发过程的敏捷性。
Sam Newman 在其《构建微服务》一书中对微服务架构中的端到端(E2E)测试提出了批判性和务实的看法。他承认,尽管E2E测试有其价值,但由于它们带来的挑战,尤其是在分布式环境中,应该谨慎使用。
Newman指出,E2E测试的问题在于它们往往会引入服务之间的耦合,进而限制了它们的独立性。因此,E2E测试可能成为开发周期中的瓶颈,延迟新功能和修复的部署。
Newman分享了一个实际经验来说明这一点:
“例如,我曾在一个单体系统中工作,我们有4,000个单元测试、1,000个服务测试和60个端到端测试。从反馈的角度来看,我们认为服务测试和端到端测试(后者在影响反馈循环方面是罪魁祸首)太多了,所以我们努力通过范围较小的测试来替代覆盖。”
他强调,尽管端到端测试只有60个,但它们被确认为延迟反馈循环的主要罪魁祸首。这些测试的影响如此之大,以至于团队选择减少大范围的测试数量,并用更小范围的测试(如单元测试和集成测试)代替。
Newman还警告了一种常见的反模式,他称之为“测试雪锥”或“倒金字塔”,即没有小范围测试或小范围测试很少,所有覆盖都由大范围测试完成。他指出,这种方法导致测试执行极其缓慢,反馈循环非常长,严重损害开发团队的效率。
他继续说:
“这些项目通常有极其缓慢的测试运行和非常长的反馈周期。如果这些测试作为持续集成的一部分运行,你不会得到很多构建结果,并且当某些东西坏掉时,构建时间的特性意味着构建可能会在很长时间内保持失败状态。”
Newman的观察强化了避免过度依赖端到端测试的必要性,尤其是在微服务环境中。当大范围测试主导测试策略时,开发敏捷性会受到影响。运行这些测试所需的时间可能如此之长,以至于当出现问题时,修复和重新运行的过程变得低效,导致长时间的停机。
对结果缺乏信心:“Flaky 测试”对产品质量的影响
实施测试套件(尤其是端到端测试)时,开发团队面临的最大挑战之一是结果的不一致性,通常被称为“Flaky 测试”。这些测试不可预测地失败,且没有明显的原因,代表了一个重大问题。它们可能在不同运行中对相同代码生成不同的结果,造成不确定性并削弱了对测试套件的信心。
不稳定测试的本质
“Flaky 测试”是任何开发团队的头疼问题,而在复杂系统(如微服务架构)中,它们的影响被放大。这些测试可能在一次运行中通过,而在下一次运行中失败,即使代码没有任何变化。它们因外部依赖不稳定、竞态条件或同步问题而失败。那么,工程界对此类问题的看法是什么?
Sam Newman 警告说,“Flaky 测试”可能表明测试设计不佳,或者系统本身不稳定。当一个团队无法信任测试结果时,测试作为确保软件质量的工具的有效性大打折扣。用于调查没有反映代码中实际问题的测试失败的时间和资源被浪费了,这可能显著减缓开发进度。以下是他的部分言论:
“不稳定的测试是敌人。当它们失败时,它们没有告诉我们太多。我们重新运行CI构建,希望它们稍后通过,结果是检查积压,我们突然发现自己有一堆破坏的功能。
当我们检测到不稳定的测试时,至关重要的是我们尽力将它们移除。否则,我们开始失去对测试套件的信任,认为‘它总是这样失败的’。拥有不稳定测试的测试套件可能会成为 Diane Vaughan 所称的‘越轨行为正常化’的受害者——随着时间的推移,我们可能会习惯于事情出错,以至于开始接受它们为正常,而不是问题。” —— Sam Newman
再引用 Martin Fowler,在其文章《消除测试中的非确定性》中,他将“Flaky 测试”描述为高效测试套件的最大敌人之一。他主张必须尽快识别并修复非确定性测试——那些产生不一致结果的测试。Fowler还讨论了这些测试可能来自各种来源,例如外部依赖(如第三方服务)、测试环境配置失败,甚至是时间同步不当。他建议,“Flaky 测试”的存在是一个警告信号,表明测试策略或应用程序本身存在问题。
操作成本与敏捷性
我的观点是:任何测试策略(尤其是测试金字塔顶端成本较高的测试,如E2E测试)中都没有“Flaky 测试”的容身之处。逻辑很简单:单元测试和集成测试可以快速执行并相对轻松调整。然而,E2E测试在执行时间、计算资源和维护方面成本高昂。如果E2E测试变得“Flaky”,处理它的操作成本将呈指数级增长,而不是单元或集成测试。
这种成本不仅限于时间和财务资源;它还直接影响团队的敏捷性。当团队无法信任测试结果时,CI/CD流水线的信心被侵蚀。工程师开始质疑每次失败,导致不必要的重新运行测试,延迟交付。这不仅减慢了新功能的实施速度,还损害了团队士气,导致挫败感和动力下降。
对最终产品质量的影响
从产品质量的角度来看,“Flaky 测试”的存在是一种严重风险。当测试不稳定时,可能会掩盖代码中的真实问题。一个偶尔通过的测试可能允许一个重大错误未被发现,最终在生产中引发关键故障。想象一个验证金融交易完整性的测试因“Flaky 测试”间歇性失败的场景。如果这种行为进入生产,它可能会造成重大损害,无论是财务上的还是声誉上的。
此外,测试套件的信心对于重构和持续代码改进过程至关重要。如果开发人员无法相信测试能够始终如一地捕捉问题,他们可能会不愿意对代码进行必要的更改,担心会引入新错误而这些错误不会被测试捕捉到。
端到端测试的可靠性是任何软件项目成功的基础。当反馈受到影响时,整个开发周期都会受到影响。
为了解决“Flaky 测试”问题,必须采用严格的测试实践,确保每个测试都有明确的目的并在受控条件下执行。这可能涉及消除外部依赖,使用Mock和Stubs来隔离被测试的代码,并持续审查测试以确保它们保持相关性和有效性。
消除“Flaky 测试”是优先事项
消除“Flaky 测试”应成为任何希望保持质量和敏捷性的开发团队的优先事项。正如你所指出的那样,在高效的质量保证策略中没有这些测试的位置,尤其是在顶端较昂贵的测试中。对测试结果的信任是对代码和最终产品信任的基础。没有这种信任,整个开发过程都会受到影响,导致产品质量下降、交付周期延长以及团队效率和士气下降。
通过采用更严格的测试实践,结合持续审查和消除“Flaky 测试”,开发团队可以保持测试的完整性,继续以敏捷且高效的方式交付高质量的软件。
高维护成本:微服务中维持E2E环境的挑战
在任何系统中维护稳定一致的测试环境都是一项艰巨任务,但在微服务生态系统中,这种复杂性被放大。在架构中,每个服务都是独立的,但相互关联,维持测试变得异常困难。这项工作不仅涉及到手动操作和技术资源,还带来时间、质量以及最终产品成功的巨大成本。
微服务生态系统中的复杂性
在分布式架构的背景下,每个服务可能都有自己的依赖关系、配置和需求。这些服务通常与多个数据库、外部API和同一生态系统中的其他服务交互。维护每个服务的稳定测试环境,并确保所有依赖项配置正确且同步,是一项真正的挑战。
例如,想象一个依赖于其他三个服务才能正常工作的服务。如果这些依赖服务中的任何一个宕机、数据不一致或运行旧版本,这可能导致测试失败,而这些失败并不反映正在测试的服务中的实际问题。更糟的是,测试环境中的手动更改(如为了“修复”问题而进行的临时调整)可能会引入难以跟踪和纠正的不一致性。
在微服务中,这种情况并不罕见,系统的分布式特性使得质量控制更加困难。每个服务中的更改可能会对其他服务产生连锁效应,增加调试和验证的难度。异构环境的结合以及对特定配置的需求增加了维护成本,并使得识别真正问题变得更加困难。
维护成本:时间、价值和质量
维护测试套件的成本不仅限于维持它所需的时间和财务资源。它还包括对团队持续高效交付价值的能力的影响。当测试需要频繁维护时,团队可能会陷入一个无休止的调整和修复循环中,耗费本可以用于实现新功能或改进的时间。
更令人担忧的是,持续维护的需求可能会导致测试质量的忽视。当工程师和QA人员被测试环境维护工作压得喘不过气来时,他们可能会倾向于“尽其所能”通过测试,即使这意味着牺牲质量。这种方法可能导致关键功能未得到充分测试,从而增加生产中出现问题的风险。
这些故障可能是毁灭性的。想象一下一个支付功能,由于测试被忽视而发布时带有错误。这不仅会直接影响用户的信任,还可能带来巨大的财务损失并损害公司的声誉。
QA视角
对于负责确保最终产品质量的QA专业人员来说,维护稳定的测试环境的挑战更加突出。他们往往是质量的守护者,负责确保最终产品符合要求的标准。在Glenford Myers的《软件测试的艺术》一书中,强调了稳定且维护良好的测试环境对于确保准确和可靠结果至关重要。Myers指出,没有受控且一致的环境,测试结果可能会误导,导致一种虚假的安全感。
虽然Myers并未直接讨论微服务,但他关于维护和测试质量重要性的原则对这种架构具有高度的相关性。在一个系统分布式且每个服务可以独立开发和部署的环境中,维护一个稳定且可靠的测试环境的挑战变得更加关键。
维护成本:Myers指出,维护测试套件的成本可能很高,但不维护它的成本更高。在微服务中,这转化为确保每个服务可以独立测试,而无需依赖可能引入错误或不一致的手动配置。
测试环境质量:测试环境的可靠性对于软件质量至关重要。这意味着测试环境需要自动配置并保持一致,避免可能破坏测试数据或环境条件的手动干预。
对质量的影响:正如Myers指出的,测试质量直接与最终产品质量相关。因此,确保测试的完整性对于避免生产中的关键问题至关重要。
对产品经理和产品负责人的影响
在软件工程中,一切都涉及到成本。虽然产品经理和产品负责人可能不会直接参与测试维护,但他们深受这些挑战的影响。测试的质量直接影响到有信心地交付新功能的能力。当测试环境不稳定时,交付时间变得不确定,迅速响应市场需求的能力大大降低。
每个参与软件项目的人都依赖快速可靠的反馈循环来规划和优先考虑下一步的开发工作。如果测试难以维护并导致不稳定,时间线拉长,导致团队间的挫败感增加。由于不断的重新规划和调整,团队士气可能下降,对有效交付价值的信心也随之下降。
此外,为了满足截止日期的压力,可能会做出仓促的决定,牺牲质量以换取“敏捷性”。这创造了一个恶性循环:生产中的问题导致更多的维护和调整,耗费了更多的时间和资源。这时,敏捷的错觉可能成为一种幻象,在快速交付中节省的时间被用于解决本可以通过更健壮的测试环境避免的问题。
维护成本的不可避免性
在微服务环境中保持质量和稳定性是一项涉及重大成本的任务,忽视这些成本可能会导致更严重的后果。这些成本不仅是技术层面的,它们渗透到整个组织中。从努力保持测试运行的工程师和QA人员,到应对延迟和妥协质量后果的产品经理和产品负责人,每个人都感受到影响。
确保稳定且高效的测试环境需要一种战略性的方法。这包括尽可能自动化,消除手动依赖,创建可轻松重现和一致配置的测试环境。从一开始就投资测试的质量并维护环境看似成本高昂,但这是必要的投资。不这样做的成本更高,无论是在时间上还是最终产品和客户信任的影响上。
因此,结论很明确:质量不容妥协。维护一个稳定且高效的测试环境的成本是不可避免的,但不维护这种质量的成本更大。确保测试可靠、一致且维护良好对于交付不仅符合期望且能够经受住时间考验的软件至关重要。
端到端测试执行时间计算:一个理论示例
让我们举一个假设的例子,它很容易适用于许多现实场景。假设我们正在处理一个负责管理优惠券的微服务。该服务有五个主要端点:
- 创建优惠券(POST /vouchers)
- 验证优惠券(GET /vouchers/{id}/validate)
- 应用优惠券(POST /vouchers/{id}/apply)
- 取消优惠券(POST /vouchers/{id}/cancel)
- 查询优惠券(GET /vouchers)
现在,假设对优惠券应用逻辑(在/vouchers/{id}/apply端点上)进行了更改。虽然更改是针对该端点的,但由于我们处理的是一个没有明确业务规则分隔的遗留系统,审慎的做法是测试所有端点,以确保更改没有引发其他服务的问题。
在这样的场景中,业务规则可能相当复杂,尤其是在确保优惠券的完整性时。例如,用户可能需要经过身份验证或授权才能访问某些资源,并且一个简单的GET调用可能在返回结果之前需要通过许多安全检查。此外,服务可能会向其他系统发出异步调用,以实时验证信息,这增加了测试过程中的时间。
让我们考虑以下有关E2E测试的细节,请注意这是一个完全假设的例子:
- 每个端点的测试场景数量:每个端点10个场景。
- 每个步骤的平均执行时间:每个步骤800毫秒(0.8秒)。
- 每个场景的步骤数量:每个场景10个步骤(其中2个步骤涉及15秒的异步调用或其他检查暂停时间)。
现在,让我们计算执行该服务所有测试场景所需的总时间:
现实中的复杂业务规则
另一个需要考虑的关键点是,取决于每个企业的规则,优惠券服务可能有更多的错误场景和复杂的规则,以确保优惠券的完整性。例如,列出优惠券的简单GET操作可能需要用户通过身份验证、具有特定权限,并且系统需要实时验证优惠券的状态。每一项检查都为测试增加了复杂性,并增加了验证它们所需的总时间。
此外,在异步通信普遍存在的环境中(如使用消息队列或事件处理数据的微服务),调试测试失败可能变得极为复杂。通常很难将失败与其真实原因(如未发送的消息)联系起来,导致系统其他部分出现意外行为。
对于软件工程师和团队负责人来说,衡量E2E测试的执行时间并监控等待或调试失败所花费的时间,对于识别影响生产力的瓶颈至关重要。通过分析这些时间,可以更好地理解测试流程可以在哪些地方优化。
例如,如果测试执行时间开始影响持续交付,可能有必要拆分测试套件或采用不同的测试方法来验证基本行为。我们将在不久后深入讨论这一主题。