我在 X(原 Twitter)上看到了 Uncle Bob(《Clean Code》作者)的一篇帖子(链接),它获得了数千次转发。帖子中提到,他不再阅读代码了。是的,就是那个曾写书强调“开发者阅读代码的频率远高于编写代码,因此代码应该易于阅读”的人。
那么 Uncle Bob 的方法是什么呢?与其阅读 AI 代理生成的代码,他要求这些代理围绕代码编写大量测试。
当然,有人同意,也有人反对。如果你不阅读代码,所有权概念就会变得模糊。但如果你阅读所有代码,又会损失大部分生产力。我无法对此做出评判或提供我的“宝贵”意见。我决定写这篇文章(尽管我很少写作)是因为这里反复出现一个话题。
现在大量讨论都围绕验证 AI 展开。当然,生物学和生物信息学中的验证与软件工程中的验证并不相同,但我认为两者存在一些有价值的共通之处。
帖子和转发中提到了程序员(包括 Uncle Bob)用来约束代理的不同测试方式。深入研究后,我发现测试的世界远比我作为生物信息学家预期的要大得多。
正确性
- 单元测试 – 以隔离方式检查单个函数或小型代码单元。它们检查逻辑、错误处理和边界情况。这是最广为人知的软件测试类型。
- 集成测试 – 检查独立模块是否能正确协同工作(例如,API 与数据库的交互)。
-
功能测试 – 从黑盒和需求的角度验证软件是否按预期工作。你向软件提供输入数据并查看输出结果。例如,如果你编写了一个新的 SNV 检测流程,你会输入测序数据并对照参考序列,检查流程是否以所需的灵敏度和特异性输出了已知的 SNV。
功能测试还包含一些值得了解的常见子类型:- 冒烟测试 – 快速检查基本功能是否正常,例如使用默认参数运行软件。
- 回归测试 – 检查最近的更新是否破坏了现有功能。
- 用户验收测试 (UAT) – 检查真实用户是否确认其需求得到满足。“真实用户”可以是社区科学家从 GitHub 下载你的工具,或在早期访问或共同开发计划中选定的合作伙伴。与其他类别一样,UAT 也有其子世界:α 测试、β 测试、业务价值验证、软件是否满足合同标准验证,以及是否符合监管要求验证(GDPR、HIPAA、FDA)。注意:尽管名称相似,验收测试(下文)由开发人员/QA 运行,而 UAT 由真实用户在真实用例中运行。
验收测试 / Gherkin 测试 – 确认完整功能满足其构建目的的业务需求。Gherkin 测试采用结构化、人类可读的格式(Given/When/Then)编写,以便非工程师无需阅读代码即可审查预期行为:Given 描述上下文,When 描述被软件捕获的特定用户操作或事件,Then 描述预期结果。
-
真值集 / 黄金数据集测试 – 并非来自 Uncle Bob,而是来自我的生物信息学经验。在生物信息学,特别是临床生物信息学中,由于湿实验中的种种原因,很难生成合成测试。因此,生物学在很大程度上依赖于真实样本数据。值得单独写一篇文章,但对于 NGS(我的主要专长)而言,一个很好的概览资源是 MDIC SRS 报告:NGS 体细胞变异参考样本。这份报告发布于 2019 年,可能略显过时——确实有很多失效链接——但我最近还使用过其中描述的样本。它描述了几类参考材料:
- 合成 DNA – 在高度表征的 DNA 中添加带有预定义遗传变异的合成 DNA 序列。
- 基因组 DNA – 带有表征变异的 DNA 材料。
- 游离细胞 DNA – 从血液样本中提取的 DNA 材料。
- 人类细胞系 – 经过广泛研究的永生化细胞系。Genome In A Bottle (GIAB) 可能是最著名的,可用于评估你的系统在高质量 DNA 上的表现。
- 组织 / 福尔马林固定石蜡包埋 (FFPE) – 许多临床样本以 FFPE 形式保存,主要出于组织学原因。然而,对于 NGS 而言,FFPE 样本制备过程具有高度破坏性,会导致高度片段化和受损的 DNA。但由于 FFPE 的临床重要性,许多生物信息学 NGS 工作流程都应在 FFPE 样本上进行测试。
- 正交验证测试 – 我的另一个生物信息学输入。当你没有良好的、市售的表征样本时,另一种验证方法是使用两种或更多独立方法检查你的输出。例如:NGS、Sanger 测序、长读长技术、光学图谱、细胞遗传学等。
压力下的鲁棒性
-
性能测试 – 在预期或峰值负载下测量响应时间、吞吐量和资源使用情况,以捕获回归。这在云计算中尤为重要,因为高估计算资源会导致超额付费,而低估则可能导致系统崩溃。作为生物信息学家,我发现最相关的子类型有:
- 负载测试 – 最简单的性能测试形式:系统(在我的案例中是生物信息学流程)在正常负载下的表现如何?例如,你期望公司每天处理 100 个 NGS 样本,每个样本约 1000 万个读对。
- 压力测试 – 系统在超出正常负载下的表现如何?如果某天需要处理 10,000 个样本,或每个样本有 1 亿个读长,会怎样?
- 拷机测试 – 将系统推向极端、高负载或边界情况,以找出它真正崩溃的地方。你的流程会在 1 亿读长样本上崩溃吗?2 亿读长样本呢?
关于这些测试成本的警告(FinOps——我最近学到的术语之一):在云环境中,测试过多场景可能会变得昂贵,长时间未被注意到的停滞进程也是如此。本地 HPC 用户和 DevOps 团队如果因你过载节点而导致其他作业长时间排队,也可能会非常不满。
严谨性检查(测试测试本身)
- 变异测试 – 故意在代码中引入小错误,并检查现有测试套件是否能捕获它们——例如,通过替换常量值、更改决策逻辑或跳过代码结构。如果测试仍然通过,则说明测试套件不够严谨。
- 测试覆盖率 – 衡量代码库中实际被测试执行的部分百分比,标记未测试区域。当然,测试质量比覆盖率数字本身更重要。
- 可重复性 / 确定性测试 – 这不在线程中,但我认为它在生物信息学和 AI 中都很重要。使用概率方法需要多次测试输入,以确定输出是否稳定。AI/ML 在本质上大多是概率性的,而生物信息学即使没有明确使用 AI/ML,也可能涉及采样或其他概率技术。
另一个变异来源是切换软件或软件包版本——即使是小版本更新,结果也可能发生巨大变化。CLIA/CAP/CLEP 法规要求对临床测试的软件变更进行重新评估。
非功能性和系统性检查
- 安全测试 – 查找漏洞而非功能正确性:敏感数据是否安全,用户权限是否按预期工作,系统是否受到代码注入保护?
-
架构检查 – 自动验证代码是否遵循定义的架构边界和依赖规则:所有模块是否按预期连接,是否遵循命名约定,软件包使用是否一致(例如,在同一个 Python 项目中未同时使用
os和pathlib实现同一目的),依赖图是否避免了循环? -
质量指标 – 通用代码健康指标(圈复杂度、依赖结构、模块大小),它们提示可维护性而无需逐行阅读代码。一些有趣的指标:
- 缺陷密度 – 每 1000 行代码发现的错误数量。
- 缺陷逃逸率 – 生产环境中发现的错误与测试中发现的错误百分比。
- 缺陷解决时间 – 修复错误所需的时间。
Google 的 DORA(DevOps 研究与评估)团队提出了一套相关但不同的指标集。虽然软件测试质量指标试图在发布前预测软件成功,但 DORA 指标评估的是代码进入生产环境后的软件交付性能。DORA 将其指标分为吞吐量和稳定性两组:
吞吐量
- 前置时间 – 将已提交的变更带到生产环境所需的时间量。
- 部署频率 – 在给定时间段内生产发布的次数。
- 失败部署恢复时间 – 缺陷解决时间的镜像,但在生产环境中测量。对我来说,它介于吞吐量和稳定性之间。
稳定性
- 变更失败率 – 缺陷逃逸率的同类指标:需要立即干预的部署/发布比例。
- 部署返工率 – 因生产事件而触发的非计划生产发布的比例。
探索性正确性
- 属性测试 – 不检查特定的输入/输出对,而是定义代码必须始终满足的通用不变量,然后生成许多不同的输入来尝试破坏它们。
对我来说,属性测试感觉类似于“正确性”部分中的单元/集成/功能测试。主要的心态转变是思考你的函数应该具有哪些属性,而不是它应该产生哪些确切的输出。例如,假设你正在测试一个查找 DNA 基序的函数。在单元测试中,你会考虑边界情况:包含基序的 DNA 片段和不包含基序的片段。在属性测试中,你会定义不变量:如果输入 DNA 包含基序,函数返回其坐标;如果不包含,则返回 -1。然后你生成遵循此规则的随机 DNA/基序对,看看函数失败了多少次。
流程和人为层面
- QA 流程 – 从用户/产品角度检查软件的更广泛的、通常是手动的质量工作流程。
- 生成产物审查 – 审查代理围绕代码生成的文档、配置和图表,作为正确性的另一个代理——一种交付物的合理性检查。
我没有使用过所有这些测试,我甚至不确定它们是否都属于典型的生物信息学流程——尽管生物信息学一直在努力与软件工程实践融合。尽管如此,我认为熟悉这些概念对于与软件团队和 AI 编码代理合作非常有价值。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.