# 测试周刊005: Google是怎么做测试的


在 Google 早期，测试并不是首要任务。公司文化高度依赖工程师的才华——聪明的人写聪明的代码。大多数情况下，这种方式都很有效。少数系统部署了集成测试，但广泛的、结构化的测试极其罕见。这就像软件开发的"狂野西部"时代。

<!--more-->

![](https://images.unsplash.com/photo-1746730406177-f8562813b938?q=80&w=1600&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D)

## Google 的测试文化：从危机到规模化的演进之路

### Google Web Server (GWS) 的觉醒时刻

在 Google 早期，测试并不是首要任务。公司文化高度依赖工程师的才华——聪明的人写聪明的代码。大多数情况下，这种方式都很有效。少数系统部署了集成测试，但广泛的、结构化的测试极其罕见。这就像软件开发的"狂野西部"时代。

但在做 Google Web Server (GWS) 这个系统时，上面的方法就玩不转了。

这是 google 搜索的核心服务，处理每个用户查询并实时返回结果。简而言之，它对 Google 的重要性就像水之于鱼一样——是绝对不可或缺。

到 2005 年，GWS 的复杂性急剧膨胀。随着系统规模增长，开发效率直线下降。发布周期变慢，bug 频繁进入生产环境。开发人员开始对自己的代码修改失去信心。曾经有一段时间，超过 80%的生产部署因为产生了影响用户的 bug 而被迫回滚。

这成为了一个关键转折点。

为了扭转局面，GWS 技术负责人做出了大胆决定：每段新代码都必须配备自动化测试，并且这些测试要持续运行。这标志着 google 测试文化的转变——从被动救火转向主动的、工程师主导的质量保障。

### 在快速变化的世界中测试

现代软件开发与光盘包装软件的时代截然不同。如今，应用程序更新频繁——有时一天多次。每次发布的背后都有一个极其复杂的代码库。

在 Google，即使是一个"简单"的服务也可能包含数百万行代码，依赖数百个库，为数十个平台的用户提供服务——每个平台都有无数种硬件、操作系统和网络配置组合。手动测试根本无法应对这种规模。

以 Google 搜索为例。完全手工测试意味着要验证从网页结果到电影信息、航班搜索、图片搜索等所有功能。这个工作量需要在每个国家、每种语言、每种设备上重复执行——同时还要确保符合无碍访问标准和安全最佳实践。

在这种规模、速度和复杂性下，唯一可行的测试方法就是自动化。

### 向工程师驱动的自动化测试转变

从 GWS 的必要修复开始，这逐渐演变成 Google 的基本原则：测试不是开发的一个阶段，而是工程工作的内在组成部分。自动化测试嵌入到开发工作流中，确保快速迭代不会牺牲可靠性。

这种方法不仅仅是为了捕获 bug——更重要的是建立信心。通过持续运行测试，工程师可以快速行动而无需担忧。新功能发布更快，质量保持高水准，用户体验良好。

## 编写、运行、响应：Google 测试哲学的核心

现代软件测试的核心是一个看似简单的循环：编写测试、运行测试、响应失败。

憋大招然后统一手工测试的时代已经过去了。今天，构建系统的工程师同时也负责系统的质量。编写测试不再是其他人的工作——它是软件开发的基本组成部分。即使在拥有专门测试团队的组织中，开发人员编写的测试仍然是质量保障的基石。

这种方法不仅在理念上正确，在实践中也是必需的。面对现代软件开发的速度和复杂性，没有其他方法能够跟得上。在整个工程组织中分担编写和维护测试的责任，确保测试能力与代码规模同步增长。

但编写和运行测试只是整个体系的一部分。自动化的真正威力体现在团队主动响应由于测试问题造成的构建失败。在大型、快速迭代的系统中，测试失败不可避免。测试策略是否有效，取决于团队处理这些失败的速度和认真程度。

让失败的测试长期存在，会快速降低信心并导致测试结果被忽视。但当团队能在几分钟内修复失败时，他们能更快地定位问题，维持对测试套件的高度信任，最终交付更好、更安全的代码。

在 Google，这种理念深入人心：将每次测试失败视为改进系统的机会，并立即采取行动。这个反馈循环——编写、运行、响应——使得工程速度能够与全球规模的软件可靠性并存。

### 长期收益：测试代码的益处

自动化测试不仅仅是工具——它是构建具有韧性、可维护性和快速迭代能力的软件系统的策略。在 Google，早期投入编写测试在项目的整个生命周期中都会产生回报：

#### 1. 减少调试，专注构建

经过充分测试的代码进入系统时 bug 更少——并且能保持这种状态。大多数缺陷在代码提交前就被捕获，节省了大量后续调试时间。

在 Google，代码不是静态的。单个组件在其生命周期中可能被修改数十次，通常由其他团队甚至自动化系统完成。没有强大的测试套件，每次变更都会带来风险。但有了测试保护，它们就像安全网一样发挥作用。如果新的变更——甚至是依赖项的变更——破坏了功能，测试基础设施会立即捕获并标记。很多情况下，有问题的变更在到达生产环境前就会被自动回滚。

#### 2. 增强对代码变更的信心

软件中变化是常态。但有了可靠的测试覆盖，团队可以自信地调整和演进系统。每个关键行为都被持续验证，支持安全的迭代开发。

这在重构时特别有用。如果变更保持了行为不变，现有测试就不需要修改——这清楚地表明重构是正确的。这种清晰性鼓励团队定期改进代码结构，而不用担心破坏功能。

#### 3. 可执行的文档

传统文档往往不可靠——经常过时、模糊或不完整。但清晰、专注的测试可以作为活跃的、可执行的文档形式。

每个测试都解释了系统在特定情况下的行为。想了解代码对某个输入应该如何响应？直接看测试用例就可以了。如果需求变更破坏了现有测试，你会立即得到明显的信号，提醒测试代码需要更新。

但是，测试用例只有在保持清晰、简洁和目标明确时才能很好地充当文档。良好的测试规范是必不可少的。

#### 4. 简化代码审查

在 Google，所有代码提交前都需要至少一次同行审查。但当代码变更配有可靠的测试时，审查过程会更顺畅、更快速。

审查者无需在脑海中跟踪每个边界情况的执行路径，他们可以直接看到测试结果。测试展示了正确性，处理了边界情况，并验证了失败模式。这让审查者能够专注于设计和清晰性，而不仅仅是正确性。

#### 5. 通过可测试性改善设计

编写测试迫使开发人员思考代码将如何被使用和维护。如果代码难以测试，通常是因为它承担了太多职责、耦合过于紧密，或者将逻辑隐藏在复杂的依赖关系后面。

可测试的代码通常就是设计良好的代码：模块化、专注且易于维护。当你因为测试暴露了设计问题而在早期修复它们时，你为自己节省了后续的重构工作和复杂性。

#### 6. 更快、更高质量的发布

在 Google，许多拥有数百名工程师的大型项目每天都向生产环境发布新版本。这种速度只有通过强大的测试套件才能实现。

自动化测试通过早期捕获回归问题和持续验证正确性，实现了快速、安全的部署。团队能够快速行动不是因为他们跳过了测试——而是因为他们在每个步骤都自动化了测试。

### 最终思考

Google 规模化的自动化测试不仅仅是大力出奇迹——它关乎智能基础设施、文化纪律以及对工程卓越的深度承诺。

## Bug 管理系统该如何设计

上周看到了[科技爱好者周刊](https://www.ruanyifeng.com/blog/2025/06/weekly-issue-352.html)讨论了这个问题，觉得深有启发。

bugzilla 的一个早期工程师，前不久写了[一篇文章](https://www.bozemanpass.com/everythings-a-bug-or-an-issue/)，介绍 Bugzilla 的四条设计原则。

他说，只有满足这四点，才是一个好的 Bug 追踪系统（bug tracking system），我感到很有启发。

（1）所有任务都要列入 Bug 追踪。不仅包括代码 Bug，还包括待开发的新功能、缺失的文档、令人困惑的用户体验、糟糕的性能等等。

换言之，Bug 追踪系统本质是任务管理，应该当作项目管理系统来用。

（2）Bug 的状态有多种，不只"打开"和"关闭"两种。

大公司的 Bug 处理流程，可能很复杂，下面是一张从 [Bugzilla 文档](https://www.bugzilla.org/docs/3.6/en/html/lifecycle.html)拷贝的流程图。

![](https://cdn.beekka.com/blogimg/asset/202506/bg2025060807.webp)

Bug 追踪系统应该足够灵活，可以自定义优先级、严重程度、是否已分配、是否有依赖等等，以便适配各种流程。

（3）每个 Bug 只能由一人负责。

这样才能明确责任，方便查看每个人正在做什么、接下来要做什么、以及最近做了什么。这也有利于培养开发者的归属感和成就感。

（4）支持自定义视图。

由于 Bug 有多种状态，追踪系统必须支持自定义视图查看，拥有强大的查询功能。

系统的默认视图：按照优先级，列出当前版本的所有没有关闭的 Bug。

开发者的个人视图：列出分配给他们的所有 Bug，同样按优先级排序。另外，用户可以保存自己的自定义视图。

### 个人感受

基本上我使用过的大部分 bug 管理系统都满足上面要求。

不过目前就职的公司由于很多非常复杂的原因，我们并没有统一使用缺陷管理系统。

取而代之的是，我们用的是飞书的多维表格来管理缺陷。

如果配置得当的话，多维表格是满足上面的 4 个要求的，凑合拿来用还是没有问题的。

但是多维表格也有不少局限性，比如

- 缺陷分析能力有限，本质上讲根本就没有这个功能；
- 长期维护的项目，缺陷记录数量会非常多，从而导致系统性能下降
- 状态流转没有状态机保障，字段之间没有联动
- 常用图表需要自己配置，很难在多个项目中共享一套配置

所以在有条件的情况下，还是使用缺陷管理系统吧，这个对缺陷的跟踪以及工作效率的提升都有正向的收益。

## pydoll 最方便使用的自动化测试工具

在使用 selenium 的时候，有个比较大的痛点是随着本机安装的浏览器的更新，各种驱动，比如 chromedriver 也需要同步更新。

对于新手来说，找到合适版本的 driver 都是一个非常大的挑战。

所以后面 selenium 官方出品一个叫做[selenium manager](https://www.selenium.dev/documentation/selenium_manager/)的工具来解决 driver 更新的问题。

因为跟 selenium 的深度整合，所以在使用上，selenium manager 是无感的。

不过在第一次使用时，有可能需要下载浏览器和驱动，所以环境初始化还是有一点点挑战的。

至于最近风头正盛的[playwright](https://playwright.dev/)，一般情况下，在第一次初始化时也会下载几个主流的浏览器，如果网络有问题的话，容错率还是不高的，有机会安装失败。

那有没有一种自动化测试工具是可以不用下载任何的浏览器和驱动，就可以直接使用的呢？

最近就正好发现了这样一款工具--[pydoll](https://autoscrape-labs.github.io/pydoll/)。

> Pydoll 通过完全摆脱 webdriver 依赖，正在革新浏览器自动化！它直接通过 DevTools 协议连接浏览器，无需外部组件，提供流畅可靠的自动化体验和原生异步性能。
> 无论是数据抓取、Web 应用测试，还是重复任务自动化，Pydoll 凭借直观的 API 和强大功能，让一切变得简单易用。

还是同样的调调，一上来就革 webdirver 的命，与 cypress 和 playwright 异曲同工。

不过试用了一下之后，发现这个工具还是有可取之处的。

官方给出的优势如下：

- **真正的简洁性**：我们不希望您浪费时间配置驱动程序或处理兼容性问题。使用 Pydoll，安装即可开始自动化。
- **真实的人类交互**：我们的算法模拟真实的人类行为模式——从点击间隔到鼠标在屏幕上的移动轨迹。
- **原生异步性能**：基于 `asyncio` 从零构建，Pydoll 不仅仅是支持异步操作——它就是为异步而设计的。
- **集成智能**：自动绕过 Cloudflare Turnstile 和 reCAPTCHA v3 验证码，无需外部服务或复杂配置。
- **强大的网络监控**：轻松拦截、修改和分析所有网络流量，让您完全掌控请求。
- **事件驱动架构**：实时响应页面事件、网络请求和用户交互。
- **直观的元素查找**：现代化的 `find()` 和 `query()` 方法，符合直觉且运行稳定。
- **健壮的类型安全**：完善的类型系统，提供更好的 IDE 支持并预防错误。

总结一下，对于测试同学和爬虫爱好者来说，下面的优点是非常有吸引力的。

- 有 chrome 就可以用，只要不是特别老旧的机器和浏览器版本，安装好 pydoll 之后就可以直接写代码了
- 支持异步操作，也就是说可以并发进行用例执行和数据爬取工作了
- 支持网络拦截器，也就是说可以用来做 api 测试了

### 代码示例

```python
import asyncio
from pydoll.browser.chromium import Chrome

async def main():
    async with Chrome() as browser:
        tab = await browser.start()
        await tab.go_to('https://github.com/autoscrape-labs/pydoll')

        star_button = await tab.find(
            tag_name='button',
            timeout=5,
            raise_exc=False
        )
        if not star_button:
            print("Ops! The button was not found.")
            return

        await star_button.click()
        await asyncio.sleep(3)

asyncio.run(main())
```

可以看到，除了 async 和 await 之外，代码的写法跟 selenium 是极其类似的。

## 言论

来自[这篇文章](https://medium.com/@contextdependence/orienting-test-planning-c72a84a98b46)

> Good test planning guides test execution in finding the most important bugs as quickly as practically possible by developing a strategy and associated testing scope to identify or mitigate relevant risks.

> 良好的测试计划通过制定策略和关联的测试范围来识别或减轻相关风险，从而指导测试执行尽快发现最重要的错误。

