测试分层
Uncle Bob: 保持测试整洁。将它们视为系统的一等公民。
原则规范摘要
间接被测试
- 不为getter和setter编写测试,它们将被间接测试
- 不为成员变量编写测试,它们将被间接测试
- 不为一行功能或显然不重要的功能编写测试,它们将被间接测试
- 不为GUI编写测试,但是需要确保将GUI代码中的所有重要处理都删除到可测试的模块中,GUI代码应当是是将数据拉到屏幕上适当位置的胶水和电线。
- 不为必须通过反复试验“弄乱”到位的任何代码编写测试,将“摆弄”的代码与确定的代码分开,并且可以为其编写测试。
- 不会为必须通过反复试验“弄乱”到位的任何代码编写测试
- 不为框架,数据库,Web服务器或其他第三方软件编写测试。将这些东西模拟出来,然后测试自己的代码,而不是他们的代码
测试的独立性/简单设计/简单测试
- 可以从测试中创建系统,但是不能从系统中创建测试。
- 测试是互不依赖的单个单元。每一个都是行为的陈述,独立于系统中的其他行为以及系统中的其他测试
- 如果丢失了生产代码,将得到一个设计更好的系统,该系统可以保持干净,因为它可以进行测试。
- 如果丢失了测试,那么生产代码就会腐烂,并且团队的速度会因生产率下降而永无休止地恶化
- 简单设计:首先使其工作,然后使其正确,然后使其体积小而快速。
- 简单设计:所有测试通过 -> 没有重复 -> 该代码表达了程序员的意图 -> 类和方法被最小化
- 简单测试:测试表达了程序员的意图 -> 测试通过 -> 测试没有重复 -> 该测试具有最少的类和方法
测试需求规范
- 软件开发中最昂贵的问题是需求问题
- 验收测试背后的想法是创建一种规范的语言,供规范人员明确地使用。
- 企业已经确实在编写测试了;他们只是使用了效率最低的执行平台:人类。
- 软件工程的第一法则:不要依赖变化很大的东西!(如:UI)
测试和生产代码解耦
- 我们不希望测试与代码耦合;因此,我们限制测试只能通过一小部分公开方法进行操作。
- 跨架构边界进行重构的成本很高
- 测试的结构不应与代码的结构相同。您拥有名为的类的事实X不应自动暗示您拥有名为的测试类XTest。
- 如果系统中一个模块的微小更改导致系统中许多其他模块的较大更改,则该系统存在设计问题。
- 测试必须具有自己的设计。它们不能简单地遵循生产代码的结构。
- 如果两个设计相同,则将它们耦合;这种耦合会导致脆弱性。测试和生产代码之间的耦合必须最小化
- 他们的行为是耦合的。但它们的结构不必耦合。甚至行为耦合也不必像您想象的那样紧密。
- 该XTest只是X的第一个客户端。我一直想减少客户端和服务器之间的耦合。所以,减少XTest和X之间的耦合的技术,会在正常的生产代码中使用。
- 其X的API已被不断完善,以至于如此狭窄和抽象,以至于它与使用它的客户端之间的耦合最小(包括XTest)
生产代码随测试增加变得更加通用
- 每个新的测试用例都会使测试套件更加受约束和更加具体。为了使新的测试用例通过,程序员努力使生产代码更通用,而不是更具体。
- 我们不会通过添加与每个测试相对应的if语句来通过测试。我们通过创新通用算法来通过测试。
- 测试的结构可以独立于生产代码的结构而变化。越来越多的测试被添加到其中,它与X的接口正在逐步缩小和抽象化
- 这些测试中的每一项都是完全具体的。这些测试中的每一项都是针对非常特殊行为的小规范。所有测试的总和X的API行为的规范。
- 随着开发的进行,测试套件越来越成为规范的一部分,它变得越来越具体。
- 随着开发的进行,测试的行为变得越来越具体。生产代码的行为变得越来越通用。这两个行为沿着泛化轴沿相反的方向移动。
- 因为没有测试套件可以指定每个必需的行为。生产代码必须将测试指定的行为概括为系统所需的所有行为的子集。
- 我们一直在编写失败的测试,以使生产代码的通用性达到不可能再编写另一个失败的测试的程度。
- 测试的结构一定不能反映生产代码的结构,因为太多的耦合会使系统易碎并阻碍重构。相反,测试的结构必须独立设计,以最大程度地减少与生产代码的耦合。
- 随着测试变得更加具体,生产代码也变得更加通用。这两个代码流沿着通用轴沿相反的方向移动,直到无法编写新的失败测试为止。
测试替身/隔离和自省/跨越边界
- 测试替身是一个非常强大的工具,具有两个主要优点:隔离和自省
- 没有替身就很难安全地测试执行危险任务的功能,例如删除文件或删除数据库表
- 没有替身就很难实现耦合有限状态机的每个状态和转换,例如通信协议。
- 模拟跨越结构上重要的边界,但不在这些边界内。
- 迫使仔细考虑什么是重要的体系结构边界。并使用多态接口实施它们。这使得可以管理跨越这些边界的依赖关系,以便可以在边界的任一侧独立部署(和开发)组件。
- 如果将模拟仅限于架构边界,则很少需要它们。
- 谨慎地进行模拟。找到一种测试方法,设计一种测试方法,您的代码,使其不需要。
测试运行速度
- 测试越慢,它们运行的频率就越低。测试运行的频率越低,您在两次测试之间编写的代码中的投入就越大;而且您将允许代码腐烂更多,以避免再次进行昂贵的测试。
- 系统解耦良好,则所有缓慢的操作都将落在可被stubbed的体系结构边界的另一端。而且这种stubbing可以将耗时数分钟的测试变成毫秒级的测试!
静态类型/动态类型
- 无论使用静态类型还是动态类型,都必须通过执行测试来证明正确性。静态类型化不会减少测试的数量。测试是关于业务规则的。
- 通过尽可能长时间地远离算法中心,逐渐增加测试的复杂性。首先处理退化,琐碎和简单的管理任务。
覆盖率/自动化验收测试/自动化集成测试/压力测试
- 您在运行覆盖率工具吗?您是否检查 是否覆盖了每个if语句和while循环?单元测试的覆盖率是否接近100%。您是否需要通过编写更多的单元测试来提高它?
- 您是否有由企业和质量检查人员编写(或至少由其验证)的自动化验收测试?这些测试的覆盖范围足够高吗?您是否需要要求质量检查人员考虑更多的极端情况来将其提高呢?
- 您是否有由架构师和开发负责人编写的自动化集成测试。这些测试是否强调了组件之间的通信路径?他们是否检查极端情况,边界问题和超时?他们是否在变化的负载下探测系统行为?
- 如果您有多个线程,是否有在单元测试和验收测试期间对那些线程施加压力的策略?例如,您是否实现了引入随机延迟和随机负载的工具,从而使竞赛条件的机会得以放大。