《Software Engineering at Google》摘抄(11)

by kevin 1. 十月 2022 13:43 >

Unlike the QA processes of yore, in which rooms of dedicated software testers pored over new versions of a system, exercising every possible behavior, the engineers who build systems today play an active and integral role in writing and running automated tests for their own code. Even in companies where QA is a prominent organization, developer-written tests are commonplace. At the speed and scale that today’s systems are being developed, the only way to keep up is by sharing the development of tests around the entire engineering staff.(与过去的QA过程不同的是,在过去的QA过程中,专门的软件测试人员仔细研究系统的新版本,练习各种可能的行为,而今天构建系统的工程师在为自己的代码编写和运行自动测试方面发挥着积极和不可或缺的作用。即使在QA是一个重要组织的公司,开发人员编写的测试也是很常见的。以当今系统开发的速度和规模,跟上的唯一方法是与整个工程人员共享测试开发。)

对于来自没有强大测试文化的组织的开发者来说,把编写测试作为提高生产力和速度的手段的想法可能看起来是对立的。毕竟,编写测试所需的时间(如果不是更长的话!)可能与实现功能所需的时间一样长。相反,在谷歌,我们发现投入于软件测试对开发人员的生产力有几个关键的好处:

  • 更少的调试
    正如你所期望的那样,经过测试的代码在提交时有更少的缺陷。重要的是,它在整个存在过程中也有较少的缺陷;大多数缺陷在代码提交之前就会被发现。在谷歌,一段代码在其生命周期内预计会被修改几十次。它将被其他团队甚至是自动代码维护系统所改变。一次写好的测试会继续带来红利,并在项目的生命周期中防止昂贵的缺陷和恼人的调试过程。对项目或项目的依赖关系的改变,如果破坏了测试,可以被测试基础设施迅速发现,并在问题被发布到生产中之前回滚。

  • 在变更中增加了信心
    所有的软件变更。具有良好测试的团队可以满怀信心地审查和接受项目的变更,因为他们的项目的所有重要行为都得到了持续验证。这样的项目鼓励重构。在保留现有行为的情况下,重构代码的变化应该(最好)不需要改变现有的测试。

  • 改进文档
    软件文档是出了名的不可靠。从过时的需求到缺失的边缘案例,文档与代码的关系很脆弱,这很常见。清晰的、有针对性的测试,一次行使一个行为的功能是可执行的文档。如果你想知道代码在某一特定情况下做了什么,看看该情况的测试。更好的是,当需求发生变化,新的代码破坏了现有的测试时,我们会得到一个明确的信号,"文档 "现在已经过时了。请注意,只有在注意保持测试的清晰和简洁的情况下,测试才能作为文档发挥最佳效果。

  • 简单审查
    在谷歌,所有的代码在提交之前都要经过至少一名其他工程师的审查(详见第九章)。如果代码审查包括彻底的测试,证明代码的正确性、边缘情况和错误情况,那么代码审查员花在验证代码是否按预期运行的精力就会减少。审查员可以验证每个案例都有一个合格的测试,而不必费心费力地在代码中对每个案例进行解读。

  • 深思熟虑设计
    为新代码编写测试是锻炼代码本身的API设计的一种实用手段。如果新代码难以测试,往往是因为被测试的代码有太多的职责或难以管理的依赖关系。设计良好的代码应该是模块化的,避免紧密耦合,并专注于特定的责任。尽早修复设计问题往往意味着以后的返工更少。

  • 快速、高质量的发布
    有了健康的自动化测试套件,团队可以放心地发布新版本的应用程序。谷歌的许多项目每天都会向生产部门发布一个新的版本--即使是有数百名工程师的大型项目,每天都会提交成千上万的代码修改。如果没有自动化测试,这是不可能的。

 

One of the lessons we learned fairly early on is that engineers favored writing larger, system-scale tests, but that these tests were slower, less reliable, and more difficult to debug than smaller tests. Engineers, fed up with debugging the system-scale tests, asked themselves, “Why can’t we just test one server at a time?” or, “Why do we need to test a whole server at once? We could test smaller modules individually.” Eventually, the desire to reduce pain led teams to develop smaller and smaller tests, which turned out to be faster, more stable, and generally less painful.(我们很早就学到的一个经验是,工程师们喜欢写更大的、系统规模的测试,但这些测试比小型测试更慢,更不可靠,更难调试。工程师们厌倦了调试系统规模的测试,问自己:"为什么我们不能一次只测试一台服务器?"或者,"为什么我们需要一次测试整个服务器?我们可以单独测试较小的模块"。最终,减轻痛苦的愿望促使团队开发越来越小的测试,结果证明,这些测试更快、更稳定,而且通常也更少痛苦。)

在谷歌,我们把每一个测试都归为一个规模,并鼓励工程师总是为一个给定的功能编写尽可能小的测试。一个测试的规模大小不是由它的代码行数决定的,而是由它的运行方式、它被允许做什么以及它消耗多少资源决定的。事实上,在某些情况下,我们对小、中、大的定义实际上被编码为测试基础设施可以在测试上执行的约束。我们稍后会讨论细节,但简单地说,小型测试在一个进程中运行,中型测试在一台机器上运行,而大型测试在任何地方运行

Code coverage is a measure of which lines of feature code are exercised by which tests. If you have 100 lines of code and your tests execute 90 of them, you have 90% code coverage.[^7] Code coverage is often held up as the gold standard metric for understanding test quality, and that is somewhat unfortunate. It is possible to exercise a lot of lines of code with a few tests, never checking that each line is doing anything useful. That’s because code coverage only measures that a line was invoked, not what happened as a result. (We recommend only measuring coverage from small tests to avoid coverage inflation that occurs when executing larger tests.)(代码覆盖率是衡量哪些特征代码行被哪些测试所执行的标准。如果你有100行代码,你的测试执行了其中的90行,你就有90%的代码覆盖率。 代码覆盖率经常被认为是理解测试质量的黄金标准,这是很不幸的。有可能用几个测试来验证大量的代码行,但从未检查过每一行是否在做任何有用的事情。这是因为代码覆盖率只衡量一行被调用的情况,而不是结果。(我们建议只测量小型测试的覆盖率,以避免执行大型测试时出现覆盖率膨胀)。)

分享到: 更多