软件测试指南
我成长于瀑布模型时代,那时测试被视为与编程分离的活动,由不同的小组执行,并在编程完成后进行。向迭代和敏捷方法的转变,特别是极限编程的影响,改变了测试的角色——提高了它的重要性,并将其与核心开发工作相结合。
我的测试理念的核心是,我们希望编写自测试代码,这意味着我们有一套可以轻松针对代码库运行的自动化测试。我们应该相信这套测试能够捕获软件中的几乎所有错误,因此当这套测试“变绿”(通过)时,我们就可以将该版本的软件发布到生产环境中。这种环境既可以显著提高软件的健壮性,也可以让我们在未来几个月内使用重构来提高生产力和响应能力。
martinfowler.com 上关于软件系统测试资料的指南。
您应该进行哪些类型的测试?
测试的形式有很多种,这取决于它们试图验证的内容、它们覆盖的代码库范围以及它们在软件过程中的作用。查看测试组合的一种流行方法是测试金字塔。
实用的测试金字塔
“测试金字塔”是一个比喻,它告诉我们将软件测试分组到不同粒度的桶中。它还给出了在每个组中应该进行多少次测试的想法。尽管测试金字塔的概念已经存在了一段时间,但团队仍然难以将其付诸实践。本文回顾了测试金字塔的原始概念,并展示了如何将其付诸实践。它展示了您应该在金字塔的不同级别中寻找哪些类型的测试,并给出了如何实现这些测试的实际示例。
微服务架构中的测试策略
在过去几年中,基于服务的架构已经转向更小、更集中的“微”服务。这种方法有很多好处,例如能够独立部署、扩展和维护每个组件,以及在多个团队之间并行开发。但是,一旦引入了这些额外的网络分区,就需要重新考虑适用于整体进程应用程序的测试策略。在这里,我们计划讨论一些方法来管理多个独立可部署组件的额外测试复杂性,以及如何在多个团队各自充当不同服务的守护者的同时保持测试和应用程序的正确性。
关于测试的多样化和奇妙形状
关于测试组合应该是金字塔形还是更像蜂窝状,存在争议。我对这场争论的第二个最大问题是,由于不清楚人们认为单元测试和集成测试之间的区别是什么,因此它变得模糊不清。
测试分类
很多人根据测试的目的和范围将测试分为不同的类别——但遗憾的是,很少有人使用相同的分类来表示相同的事物。以下是我对事物进行分类的方式,我尝试使本网站上的资料与这些类别保持一致。
单元测试
单元测试在软件开发中经常被提及,并且是我在编写程序的整个过程中都熟悉的一个术语。然而,像大多数软件开发术语一样,它的定义非常模糊,我发现当人们认为它的定义比实际更严格时,往往会出现混淆。
集成测试
集成测试确定独立开发的软件单元在相互连接时是否能正常工作。这个术语甚至被软件行业中分散的标准所模糊,因此我一直避免在我的写作中使用它。特别是,许多人认为集成测试的范围必然很广,而实际上可以通过更窄的范围更有效地完成集成测试。
契约测试
使用测试替身的最常见情况之一是与外部服务通信时。通常,此类服务由不同的团队维护,它们可能会受到缓慢且不可靠的网络的影响,并且自身也可能不可靠。这就是测试替身很方便的原因,它可以防止您自己的测试变得缓慢且不可靠。但是,针对替身进行测试总是会引发这样的问题:替身是否确实是外部服务的准确表示,以及如果外部服务更改其契约会发生什么?
面向业务的测试
面向业务的测试是一种测试,旨在用作与开发团队的非编程成员(如客户、用户、业务分析师等)进行沟通的辅助工具。自动化后,它们会以面向领域的术语描述系统,而忽略系统本身的组件架构。面向业务的测试通常用作验收标准,此类测试通过表明系统提供了客户期望的功能。
皮下测试
我使用皮下测试来表示在应用程序 UI 下运行的测试。这在对应用程序进行功能测试时特别有用:当您想要测试端到端行为,但很难通过 UI 本身进行测试时。
实现测试
良好测试文化的重要组成部分是设计测试基础架构,使其易于编写和高效地进行测试。
模拟对象不是存根
“模拟对象”一词已成为描述用于测试的模拟真实对象的特殊情况对象的流行词。现在,大多数语言环境都有框架,可以轻松创建模拟对象。然而,人们常常没有意识到的是,模拟对象只是一种特殊情况测试对象,它支持不同风格的测试。在本文中,我将解释模拟对象是如何工作的,它们是如何鼓励基于行为验证的测试的,以及围绕它们的社区是如何使用它们来开发不同风格的测试的。
xUnit 测试模式
本世纪,程序员测试显著兴起,程序员开始负责编写自己代码的测试。我们发现这有助于减少错误,更容易思考 API 设计,并使重构成为可能。实现这一目标的一个重要部分是 xUnit 系列简单测试框架,它起源于 Kent Beck 和 Erich Gamma 的工作。但是,尽管 xUnit 是一个简单的工具,但有效地使用它需要经验和技巧。Gerard Meszaros 是这些工具和使用模式帮助人们学习软件设计技术的理念的早期采用者。它提供了一个词汇表来帮助我们交流如何使用测试,并为经验丰富的从业者将他们的知识传递给新一代奠定了基础。
Goto Fail、Heartbleed 和单元测试文化
2014 年初发现了两个计算机安全漏洞:苹果的“goto fail”错误和 OpenSSL 的“Heartbleed”错误。两者都可能导致广泛而严重的的安全故障,其全部程度我们可能永远无法知晓。鉴于其严重性,软件开发行业必须反思如何才能检测到这些漏洞,以便我们能够提高在未来防止此类缺陷的能力。本文探讨了单元测试可以发挥的作用,展示了单元测试,更重要的是单元测试文化,如何识别这些特定的错误。它接着探讨了这种文化的成本和收益,并描述了这种文化是如何在谷歌扎根的。
消除测试中的不确定性
自动化回归测试套件在软件项目中可以发挥至关重要的作用,它不仅对减少生产中的缺陷很有价值,而且对演进式设计也至关重要。在与开发团队的交谈中,我经常听到关于非确定性测试的问题——有时通过、有时失败的测试。如果不加控制,非确定性测试会完全破坏自动化回归测试套件的价值。在本文中,我将概述如何处理非确定性测试。最初,隔离有助于减少它们对其他测试的损害,但您仍然需要尽快修复它们。因此,我将讨论非确定性的常见原因的处理方法:缺乏隔离、异步行为、远程服务、时间和资源泄漏。
测试癌
随着我的职业生涯转向全职写作,我经常担心自己会与日常软件开发的现实脱节。我见过其他知名人士与现实失去联系,我担心自己也会有同样的命运。我对这一点最大的抵制力量是 Thoughtworks,它就像一剂定期的现实剂量,让我脚踏实地。
Thoughtworks 也是一个来自该领域的创意来源,我喜欢写一些我的同事发现和开发的有用的东西。通常,这些都是有用的想法,我希望我的一些读者能够使用。我今天的话题不是一个那么令人愉快的话题。这是一个问题,而且是一个我们没有答案的问题。
测试覆盖率
我时不时会听到有人问他们应该以什么样的测试覆盖率(也称为代码覆盖率)为目标,或者自豪地陈述他们的覆盖率水平。这样的说法没有抓住重点。测试覆盖率是一个有用的工具,用于查找代码库中未经测试的部分。测试覆盖率作为一个数字化的指标来衡量测试的好坏,用处不大。
测试影响分析的兴起
测试影响分析 (TIA) 是一种加速构建测试自动化阶段的现代方法。它的工作原理是分析源代码的调用图,以确定在更改生产代码后应该运行哪些测试。微软已经对这种方法进行了广泛的研究,但开发团队也有可能以相当低的成本实现一些有用的东西。
TDD 已死?
Ruby on Rails 的创始人 David Heinemeier Hansson 在 RailsConf 大会上发表主题演讲,宣称 TDD 已死。这在 Rails 和更广泛的软件开发社区中引起了意料之中的巨大争议。这也引发了 David、Kent 和我之间的一些有趣对话。我们认为这些对话很有趣,其他人可能也想看看,所以录制了一系列视频环聊,讨论 TDD 在软件开发中的作用。
页面对象
当您针对网页编写测试时,您需要引用该网页中的元素才能点击链接并确定显示的内容。但是,如果您编写的测试直接操作 HTML 元素,则您的测试将很容易因 UI 的更改而变得脆弱。页面对象使用特定于应用程序的 API 包装 HTML 页面或片段,允许您操作页面元素而无需深入研究 HTML。
谦卑对象
有些程序元素天生就难以测试,甚至无法测试。因此,这些元素中的任何逻辑都容易出现错误并且难以改进。为了缓解这个问题,请将尽可能多的逻辑从难以测试的元素中移出,并移入代码库中其他更友好的部分。通过使不可测试的对象变得谦卑,我们减少了它们隐藏邪恶错误的可能性。
现代模拟工具和黑魔法
现代模拟工具对我们处理遗留代码的能力的积极影响,以及使用这些工具可能带来的负面影响。
时钟包装器
如果您需要在代码中获取当前日期或时间,请不要直接访问获取该数据的系统例程。在其周围放置某种形式的包装器,允许您通过将“当前日期/时间”设置为特定值来覆盖它。这对于简化测试非常重要。
对象母体
对象母体是测试中使用的一种类,用于帮助创建用于测试的示例对象。
测试资源池
我正在翻阅一些旧笔记,偶然发现了 Rich Garzaniti 给我的一个简单但有用的技巧。
自动化测试还不够
尽管我主张将自动化自测试作为有效开发工作的一个核心部分,但此类测试并不能完成您在测试或质量保证中需要做的所有事情。自动化测试提供了一个很好的错误捕获网,但您需要探索性测试来确定该网是否真的覆盖了您需要的所有内容。虽然测试曾经是在代码投入生产之前应该完成的事情,但我们现在将监控和可观察性视为确定正在运行的软件的健康状况的重要工具。
生产中的质量保证
传统上,QA 专注于在软件发布到生产环境之前对其进行测试,以查看它是否已准备好发布。但越来越多的现代 QA 组织也将注意力集中在生产环境中运行的软件上。通过分析日志和其他监控工具,他们发现了质量问题并向开发组织突出显示。这种方法在使用持续交付快速可靠地将新版本软件投入生产的组织中特别有效。
探索性测试
探索性测试是一种强调学习、测试设计和测试执行的快速循环的测试风格。探索性测试不是试图验证软件是否符合预先编写的测试脚本,而是探索软件的特性,发现将被归类为合理行为或故障的内容。
面向领域的 Observability
我们软件系统中的可观察性一直很有价值,在这个云和微服务的时代更是如此。然而,我们添加到系统中的可观察性往往在本质上是相当低级和技术性的,而且它似乎经常需要在我们代码库中充斥着对各种日志记录、检测和分析框架的粗糙、冗长的调用。本文描述了一种模式,可以清理这种混乱,并允许我们以一种干净、可测试的方式添加与业务相关的可观察性。
综合监控
综合监控(也称为语义监控)定期针对实时生产系统运行应用程序自动化测试的子集。结果被推送到监控服务,该服务会在出现故障时触发警报。此技术将自动化测试与监控相结合,以便检测生产中的业务需求故障。