持续交付.发布可靠软件的系统方法

第一章

1.1 引言

  • 主要描述了一些公司的现状,介绍了这本书描述了软件从开发到发布这一过程的有效模式,和本书的最佳实践
  • 在这里插入图片描述

1.2 一些常见的发布反模式

1.2.1 反模式:手工部署软件
  • 描述了部署过程都比较复杂,许多组织/个人都是用手工方式发布软件,因此可能会造成很多问题,这种称为反模式。具体特征如下:
    *在这里插入图片描述
  • 自动化部署是一个必不可少的目标,具体原因如下:
    在这里插入图片描述
    在这里插入图片描述
1.2.2 反模式:开发完成之后才向类生产环境部署
  • 描述了软件开发完成了,第一次部署到测试运行环境时,这种模式中,经常出现下面这些情况:

    • 如果测试人员一直参与了在此之前的过程,那么他们已在开发机器上对软件进
      行了测试。
    • 只有在向试运行环境部署时,运维人员才第一次接触到这个新应用程序。在某
      些组织中,通常是由独立的运维团队负责将应用程序部署到试运行环境和生产
      环境。在这种工作方式下,运维人员只有在产品被发布到生产环境时才第一次
      见到这个软件。
    • 有可能由于类生产环境非常昂贵,所以权限控制严格,操作人员自己无权对该环
      境进行操作,也有可能环境没有按时准备好,甚至也可能根本没人去准备环境。
    • 开发团队将正确的安装程序、配置文件、数据库迁移脚本和部署文档一同交给
      那些真正执行部署任务的人员,而所有这些都没有在类生产环境或试运行环境
      中进行过测试。
    • 开发团队和真正执行部署任务的人员之间的协作非常少。

    每当软件部署带到运行测试环境时,都需要很多人来完成这项任务。在大型组织中,这种部署责任通常落在多个分立的团队肩上。由于部署工作中的很多步骤根本没有在试运行环境上测试过所以常常遇到问题,从而导致部署失败。
    在部署的期间可能遇到系统设计对生产环境的错误假设,比如:部署的某个应用软件是用文件系统做数据缓存的。这在开发环境中是没有什么问题的,但在集群环境中可能就不行了。解决这类问题可能要花很长时间。

对策:将测试、部署和发布活动也纳入到开发过程中,让它们成为开发流程正常的一部分。这样的话,当准备好进行系统发布时就几乎很少或不会有风险了,因为你已经在很多种环境,甚至类生产环境中重复过很多次,也就相当于测试过很多次了

1.2.3 反模式:生产环境的手工配置管理
  • 略(实际不会涉及到)
1.2.4 我们能做得更好吗
  • 举例说明了一个客户的例子。最后帮助客户实现了一个善的自动构建、部署、测试和发布系统,只花了七秒钟就将应用程序部署到了生产环境中,假如部署失败了,无论是什么原因,我们都可以在同样短的时间里回滚。

1.3 如何实现目标

本书有两个目标,其中之一就是找到减少周期时间的方法。(周期时间是从决定进行变更的时刻开始,包括修正缺陷或增加特性,直至用户可以使用本次变更后的结果。)

  • 减少了周期时间,意味着会经常频繁的发布版本。我们就需要自动化的发布版本,这样做减少了人工犯错的几率,以及提高了时间效率
  • 频繁交互还有一个好处,版本之间差异很小,且容易回滚。频繁发版本也会加快反馈速度,而客户也需要它。
    对于频繁的自动发布,反馈是至关重要的,反馈的三个标准:
    • 无论什么样的修改都应该触发反馈流程;
    • 反馈应该尽快发出;
    • 交付团队必须接收反馈,并依据它作出相应的行动。
1.3.1 每次修改都应该触发反馈流程
  • 什么是反馈流程?它是指完全以自动化方式尽可能地测试每一次变更。根据系统的不同,测试会有所不同,但通常至少包括下面的检测。
     创建可执行代码的流程必须是能奏效的。这用于验证源代码是否符合语法。
     软件的单元测试必须是成功的。这可以检查应用程序的行为是否与期望相同。
     软件应该满足一定的质量标准,比如测试覆盖率以及其他与技术相关的度量项。
     软件的功能验收测试必须是成功的。这可以检查应用是否满足业务验收条件,
    交付了所期望的业务价值。
     软件的非功能测试必须是成功的。这可以检查应用程序是否满足用户对性能、
    有效性、安全性等方面的要求。
     软件必须通过了探索性测试,并给客户以及部分用户做过演示。这些通常在一
    个手工测试环境上完成。此时,产品负责人可能认为软件功能还有缺失,我们
    自己也可能发现需要修复的缺陷,还要为其写自动化测试来避免回归测试。
1.3.2 必须尽快接收反馈
  • 快速反馈的关键是自动化
  • 当然,实现这样的部署流水线是需要大量资源的,尤其是当有了全面的自动化测
    试套件之后。部署流水线的关键目的之一就是对人力资源利用率的优化:我们希望将
    人力释放出来做更有价值的工作,将那些重复性的体力活交给机器来做。
    对于整个流水线中的提交(commit)阶段,其测试应具有如下特征。
     运行速度快。
     尽可能全面,即75%左右的代码库覆盖率。只有这样,这些测试通过以后,我
    们才对自己写的软件比较有信心。
     如果有测试失败的话,就表明应用程序有严重问题,无论如何都不能发布。也
    就是说,像检查界面元素的颜色是否正确这类测试不应该包含在这个测试集合
    当中。
     尽可能做到环境中立。这个环境没必要和生产环境一模一样,可以相对简单廉
    价一些。
    相对而言,提交阶段之后的测试一般有如下这些特点。
     它们通常运行更慢一些,所以适合于并行执行。
     即使某些测试有可能失败,但在某种场合下,我们还是会发布应用程序。比如
    某个即将发布的版本有一个不稳定的修复,会导致其性能低于预先定义的标准,
    但有时我们还是会决定发布这个版本。
     它们的运行环境应该尽可能与生产环境相同。除了测试功能以外,它同时还会
    对部署过程以及对生产环境的任何修改进行测试。
1.3.3 交付团队必须接收反馈并作出反应
  • 最好在一起进行开发工作,或者经常碰头。每个迭代需要进行回顾。
1.3.4 这个流程可以推广吗

1.4 收 效

1.4.1 授权团队

 测试人员可以选择性地部署较旧的版本,以验证新版本上的功能变化。
 技术支持人员可以自己部署某个已发布的版本,用于重现缺陷。
 作为灾难恢复手段,运维人员可以自己选一个已知的正确版本,将其部署到生
产环境中。
 发布方式也变成一键式的了。

1.4.2 减少错误
  • 述说:一个比特的影响客户例子
    排查了很久发现是一个第三方库的版本不一致
  • 述说:手工配置管理在不同环境导致的配置问题
    使用了不同环境的配置文件进行配置导致的悲剧
1.4.3 缓解压力
1.4.4 部署的灵活性
  • 述说了,灵活的部署方式可以使用笔记本当作服务器来进行验收测试。
1.4.5 多加练习,使其完美
  • 主要述说了无论部署到什么样子的目标环境都要支持
1.5 候选发布版本
  • 传统的候选发布版本
    • 通常需要以较长的时间验证过程来确保软件满足质量要求并且实现了全部功能需求,之后才能确定能够发布的候选版本,开发完成在进行测试会降低应用程序的质量,最好在缺陷被引用时,就发现并且解决掉。
  • 每次提交代码都可能产生一个可发布的版本
1.6 软件交付的原则
软件部署包括三件事:

 提供并管理你的软件所需要的运行环境,这包括硬件配置、所依赖的软件、基
础设施以及所需的外部服务;
 将你的应用程序的正确版本安装在其之上;
 配置你的应用程序,包括它所需要的任何数据以及状态。

1.6.3 把所有的东西都纳入版本控制
  • 整个过程中所需的东西全部保存在某种形式的版本
    存储库中,包括需求文档、测试脚本、自动化测试用例、网络配置脚本、部署脚本、
    数据库创建、升级、回滚和初始化脚本、应用程序所依赖的软件集合的配置脚本、库
    文件、工具链以及技术文档等。所有这些内容都应该受到版本控制,与每次构建结果
    相关的版本都应可以识别。也就是说,这些变更集(change set)都应该有唯一标识,
    比如构建号、版本控制库中的版本号。
1.6.4 提前并频繁地做让你感到痛苦的事
  • 简单描述,什么事情最痛苦就应当提前开始做。而不是等到最后来做,最后一定会让你痛苦。
    如果创建应用程序的说明文档是你的痛点,那么每开发一个功能时就应写好文档,而不是留到最后一起写。把一个功能的说明文档也作为“DONE”的一个验收条件,并尽可能自动化这个过程。

自动化:要想做到这一点很可能会花很多功夫,但你又无法和客户说:“因为我要做自动化,所以就不能交付新功能了。”所以,你可能需要选择一个中期目标,比如每隔几周做一次内部发布。假如你现在就是这么做的,那么就每周做一次。逐步地走向理想状态,即使是一小步一小步地进行,也会带来很大的价值

1.6.5 内建质量
  • 尽可能早的发现问题,越早发现成本越低
  • 内建质量2个推论:
    • 测试不是一个阶段(开发完成的阶段),如果测试留到最后为时晚矣,因为可能根本没有时间修复那些刚被发现的问题。
    • 测试不主要时测试的责任,团队每个人都因该对应用程序的质量负责。
1.6.6 “DONE”意味着“已发布”
  • 只有正真的交付给用户了才能算是"Done"
  • 一件事情的完成与否,并不是一个人能控制得了的,它需要整个交付团队共同来完成。
1.6.7 交付过程是每个成员的责任
  • 团队中的成员应该有共同的目标(避免相互指责浪费时间)
  • DevOps运动的核心原则之一:
    • 每个人都可以一眼就知道应用程序所处的状态,比如其健康状况、各种构建版本、构建通过了哪些测试、它们可被部署到的环境的状态。鼓励所有参与软件交付整个过程中的人进行更好的协作。
1.6.8 持续改进
  • 在交付过程中,整个团队应该定期地坐在一起,召开回顾会议,反思一下在过去
    一段时间里哪些方面做得比较好,应该继续保持,哪些方面做得不太好,需要改进,
    并讨论一下如何改进
  • 每个改进点都应该有一个人负责跟踪,确保相应的改进活动能够被执行。当下一次团队坐在一起时,他们应该向大家汇报这些活动的结果。这就是众所周知的戴明环:计划执行检查处理(PDCA)。
总结
  • 通过采用自动构建、测试和部署技术,可以获得很多益处,我们将能够验证变化,
    重现各种环境中的部署过程,在很大程度上减少产品出错的机会

第二章

2.1
2.2 使用版本控制

省略第二章全部内容

3 持续集成

3.2 实现持续集成

持续集成:代码版本控制,单元测试,代码规范检查,项目的发布部署等工作步骤,有机的组织起来,并且利用其调度性可作自动化处理,它还有强大的日志记录功能,能将集成结果及时地反馈给项目管理人员和项目开发人员

3.2.1 准备工作
  1. 版本控制
    与项目相关的所有内容都必须提交到一个版本控制库中,包括产品代码、测试代
    码、数据库脚本、构建与部署脚本,以及所有用于创建、安装、运行和测试该应用程
    序的东西
    2.自动化构建
  • 必须满足如下条件:人和计算机都能通过命令行自动执行应用的构建、测试以及部署过程。
  • 给出了几个注意项:
     要能在持续集成环境中以自动化的方式来执行整个构建过程,以便出现问题时
    能够审计。
     应将构建脚本与代码库同等对待。应该对它进行测试,并不断地重构,以使它
    保持整洁且容易理解,而集成开发环境自动生成的构建过程基本上无法做到这
    一点。项目越复杂,这项工作就越重要。
     使理解、维护和调试构建过程更容易,并有利于和运维人员更好地协作。
  1. 团队共识
  • 持续集成不是一种工具,而是一种实践
  • 它需要开发团队能够给予一定的投入并遵守一些准则,需要每个人都能以小步增量的方式频繁地将修改后的代码提交到主干上,并一致认同“修复破坏应用程序的任意修改是最高优先级的任务”

7 提 交 阶 段

7.1 引 言
  • 当代码一次提交(向版本控制库的一次提交)时,当它结束时,你要么得到失败报告,要么得到后续测试和发布阶段的二进制产物和可部署的程序集,
  • 本章更详细地讨论如何创建有效的提交阶段和提交测试
    • 提交阶段需要经过的步骤
       编译(如果需要的话),并在集成后的源代码上运行提交测试;
       创建能部署在所有环境中的二进制包(如果使用需要编译的语言,则包括编译和组装);
       执行必要的分析,检查代码库的健康状况;
       创建部署流水线的后续阶段需要使用的其他产物(比如数据库迁移或测试数据)。
    • 提交阶段的步骤任务,由持续集成服务器通过调用相应的构建脚本组织在一起,如果成功。二进制包和结果报告就被保存在你的中央仓库中,以供交付团队和部署流水线的后续阶段使用。
      对开发人员来说,提交阶段是开发环节最重要的一个反馈循环,它会为开发人员引入的最常见错误提供迅速反馈
7.2 提交阶段的原则和实践
  • 提交阶段的首要目标是要么创建可部署的产物,要么快速失败并将失败原因
    通知给团队。
7.2.1 提供快速有用的反馈
  • 提交测试的失败通常是由以下三个原因引起的:
    (1) 由于语法错误导致编译失败
    (2) 由于语义错误导致一个或多个测试失败
    (3) 由于应用程序的配置或环境方面(包括操作系统本身)的问题引起
    提交测试一结束,就要通知开发人员。并提供简明的失败原因报告

  • 引入错误后,越早发现它,越容易修复它。主要来至如下的一些因素:

    • 才开发完成时,对代码的上下文理解印象比较少深,找到错误原因的方法也比较简单。
    • 如果是(开发人员)修改了一些内容并因此导致某个测试失败,而失败原因不是非常明显,最自然的做法就是查看从最后一次成功提交后到目前为止所有修改过的内容,来缩小搜查范围。 这一点非常有用,这种工作模式下。如果开发一时找不出来问题,测试人员可以通过最近的提交来缩小排查范围!
    • 如果开发人员按照建议,频繁提交修改的话,每次变更都会比较小,变更的范围就仅限于该开发人员自己修改的代码。也就是说,修复那些在提交阶段发现的问题,要比修复那些由后续运行大量测试的阶段发现的问题简单得多。
  • 什么是 预测试提交或试飞构建
    预测试提交或试飞构建:提交阶段之前,开发环境中发现编译警告(如果适用)或语法错误,或者提交之前的进行的自测或功能验证。很多新的持续集成服务器也提供称为预测试提交或试飞构建的功能,简单描述就是:提交之前就运行一下提交测试

  • 持续集成对构建失败的定义

    • 我们一般会把提交阶段分成一系列的任务(具体包括哪些任务就因项目而异了),比如编译、运行单元测试等。只有在某个错误让提交阶段的其他任务无法执行时,我们才会让提交阶段停下来,比如编译错误,否则就直至提交阶段全部运行完后,才汇总所有的错误和失败报告,以便可以一次性地修复它们。
7.2.2 何时令提交阶段失败
  • 提交阶段的失败具体定义如下
    • 编译错误、
    • 测试失败
    • 或者环境问题
      否则就应该让提交阶段成功通过并报告一切OK,
      但是有可能 提交阶段只有两种状态似乎太严格了,因为有可能提交测试只是测试了一小部分代码呢?如果代码质量不高呢?如果编译成功,但有很多编译警告,我们也能视为成功吗?其实有很多争论。有人认为,在提交阶段结束时,应该提供更丰富的信息,比如关于代码覆盖率和其他度量项的一些图表。在实际中这很复杂。这里给出了另一种解决办法
    • 当某次构建的编译警告的数量比前一次增多或者没有减少时,就让提交阶段失败(这就是“渐进式”实践)
7.2.3 精心对待提交阶段
  • 提交阶段 必须要经过的流程测试包括(不限于)如下:

    • 构建用的脚本
    • 运行单元测试
    • 静态分析
      ** 如上脚本需要小心维护,如果构建脚本设计得很差,还没得到很好维护的话,那么保持它能够正常工作所需投入的精力会呈指数级增长,随着项目的进行,要不断努力地改进提交阶段脚本的质量、设计和性能。一个高效、快速、可靠的提交阶段是提高团队生产效率的关键,对于测试而言,要想令提
      交阶段在较短时间内完成,并尽早捕获任何问题的话,就要有一些创造性,比如仔
      细地选择和设计测试用例。**
  • 脚本需要模块化
    那些经常使用但很少变化的常见任务与经常需要修改的任务(比如向代码库中增加模块)分开

7.2.4 让开发人员也拥有所有权
  • 需要让开发人员擅长管理这些脚本的运行环境,如果只有那些专家才有权维护持续集成系统的话,那就是一种失败的管理方式。
  • 开发/测试人员对提交阶段(流水线基础设施的其他部分)拥有所有权是至关重
    要的,这与交付团队的工作和生产效率是紧密联系在一起的,开发人员和运维都必须要习惯构建系统的维护工作,而且要对其负责。
7.2.5 在超大项目团队中指定一个构建负责人
  • 在校团队(20-30人)中,自组织就可以了,如果构建失败很容易找到人,但是在大团队中,就必须要指定一个构建负责人。
7.3 提交阶段的结果
  • 测试结果(假如测试失败,这些结果是找出哪里出了错的重要信息)
  • 代码库的分析报告(包括测试覆盖率、圈复杂度、复制/粘贴分析、输入和输出耦合度以及其他有助于建立健康代码库的度量项。)
  • 提交阶段生成的二进制包应该在该部署流水线的实例中会一直被重用,而且可能会上线交互用户。
制品库

提交阶段输出的包,需要保持到某个地方。版本库并不合适。因为它会让你的磁盘空间很快被吃掉,而且有些版本控制系统对二进制文件支持不佳。另外还有几个理由:
 它仅保存某些版本,而不是全部。(发布或则部署的某个流程失败了,不需要保留)
 已发布的软件究竟是由版本控制库中的哪个版本产生的。版本控制库作为部署流水线的一部分,我们已经把所有东西都提交到版本控制库了,而将更多修订版本与相应的流水线实践关联在一起会让这个流程更加复杂。

  • 下面是一个候选发布版本在理想情况下在部署流水线中成功走向生产环境的每一
    步。
    (1) 交付团队的某个人提交了一次修改。
    (2) 持续集成服务器运行提交阶段。
    (3) 成功结束后,二进制包和所有报告和元数据都被保存到制品库中。
    (4) 持续集成服务器从制品库中获取提交阶段生成的二进制包,并将其部署到一个
    类生产测试环境中。
    (5) 持续集成服务器使用提交阶段生成的二进制包执行验收测试。
    (6) 成功完成后,该候选发布版本被标记为“已成功通过验收测试”。
    (7) 测试人员拿到已通过验收测试的所有构建的列表,并通过单击一个按钮将其部
    署到手工测试环境中。
    (8) 测试人员执行手工测试。
    (9) 一旦手工测试也通过了,测试人员会更新这个候选发布版本的状态,指示它已
    经通过手工测试了。
    (10) 持续集成服务器从制品库中拿到通过验收测试(根据部署流水线的配置,也可
    能是手工测试)的最新候选发布版本,将其部署到生产测试环境。
    (11) 对这个候选发布版本进行容量测试。
    (12) 如果成功了,将这个候选版本的状态更新为“已通过容量测试”。
    (13) 如果部署流水线中还有后续阶段的话,一直重复这种模式。
    (14) 一旦这个候选发布版本通过了所有相关阶段,把它标记为“可以发布”,并且
    任何被授权的人都能将其发布,通常是由质量保证人员和运维人员共同批准。
    (15) 一旦发布以后,将其标记为“已发布”。
  • 为简单起见后续阶段不串行执行也是正常的。(比如,手工测试和容量测试就可以被验收测试的成功同时触发。另外,测试团队还可以将不同版本的候选发布版本部
    署到他们的环境中。)
7.4 提交测试套件的原则与实践
  • 重要的原则和实践
    • 大部分应由单元测试组成,原因有二:
      1:单元测试运行速度非常快,因为测试套件运行不够快而令构建失败。
      2: 它们应覆盖代码库的大部分(经验表明一般为80%左右),让你有较大的信心,能够确定一旦它通过后,应用程序就能正常工作。
      综合实际结论:单元测试占了自动化测试中相当大的比例。但由于它们执行得非常快,所以单元测试套件应该能在几分钟内就结束。
  • 设计能够快速运行的提交测试并不总是那么简单的事情,下面介绍几种策略:
    • 大部分都是为了达到一个共同的目标:将指定测试的范围最小化,并让它尽可能
      聚焦于系统的某个方面
    • 尤其注意的点:运行的单元测试不应该与文件系统、数据库、库文件、框架或外部系统等打交道。(所有对这些方面的调用都应该用测试替身代替,比如模拟对象(mock)和桩等。)
7.4.1 避免用户界面

** 不建议通过用户界面来进做提交测试 **

  • 用户界面测试的困难来自两方面:
    • 它会涉及很多组件或软件的多个层次。这样是容易出问题的,因为要花很多时间和精力去准备各种各样的组件或数据,才能让测试运行起来。
    • 用户界面是提供给用户手工操作的,而手工操作的速度与计算机操作的运
      行速度相比,是相当慢的。
7.4.2 使用依赖注入
  • 使用依赖注入,好处很多。不做过多描述…
7.4.3 避免使用数据库
  • 当然,提交测试中的单元测试不应当依赖于数据库。再次解释了依赖注入真香啊
7.4.4 在单元测试中避免异步
  • 单元测试异步行为尽量避免,这是当然的。不过C#真香,Task可以等待其返回结果。香香香
7.4.5 使用测试替身
  • 又说了,依赖注入的香,并且变相说了下,函数单一是软件代码优秀的表现。当然我也是这样做的
  • 少用桩技术
  • 模拟技术(mocking)使用它的动机是希望广泛利用与桩类似的技术,而又不需要我们自己写很多桩代码。让计算机为我们自动生成这些桩,而不是自己写,这样不是更好吗?
    • 几种模拟技术工具集,比如Mockito、Rhino、EasyMock、JMock、NMock和Mocha等。使用模拟技术,你就可以说:“给我构建一个对象,让它假装就是某某类型的一个类。”
7.4.6 最少化测试中的状态

理想情况下,单元测试应聚焦于断言系统的行为

  • 举例:测试就是为系统中的某个组件提供一些输入信息,然后得到一定的返回结果
    • 所以在写测试时,你就会组织一下相关的数据结构,以便以正确的形式提交输入信息,然后再把结果与你期望的进行比较,这里会存在一个问题:如果处理不当的话,这个系统及其相关的测试会变得越来越复杂。
  • 理想的测试应该能很容易和快速地进行测试准备,而清理工作也应该更快、更容易。对于结构良好的代码来说,其测试代码往往也非常整洁有序。如果测
    试看起来繁琐复杂,那可能是系统设计有问题。
    总结:这是个很难定性的问题。我们的建议是设法让测试中的这种对状态的依赖
    最小化。
7.4.7 时间的伪装
  • 对于一些业务对时间比较敏感的,时间对函数应该是依赖注入的
7.4.8 蛮 力
  • 运行速度稍慢一点儿的提交测试可能优于通过优化测试或减少发现的缺陷数来追
    求运行速度的提交测试。
  • 通常提交测试在十分钟内完成,这基本是上一个上限,理想应该在五分钟内。时间太长有二个弊端:
    (1) 提交频率变低
    (2) 如果提交阶段的用时远远超过十分钟,他们可能就不再关注提交
    阶段通过与否了。
  • 有二招解决时间问题:
    • 首先,将它分成多个套件,在多台机器上并行执行这些套件(新的持续集成服务器都有“构建网格”功能,直接支持这种做法。)
    • 作为构建优化过程的一部分,将那些运行时间比较长且不经常失败的测试放到验收测试阶段运行。然而,需要注意的是,这会导致需要更
      长的时间才能知道这些测试是否失败了。
7.5 小 结
  • 提交测试应该聚焦于一点,即尽快地捕获那些因修改向系统中引入的最常见错误
  • 提交测试如果大于5分钟,估计有人会抱怨,需要倾听这些反馈,并想办法让提交测试更快。

8 自动化验收测试

8.1 引 言

自动化验证测试通常是每个以通过提交测试的软件版本上执行的

  • 验收测试与功能测试或单元测试有什么不同呢?
    • 验收测试:是验证一个用户故事或需求的验收条件是否被满足。验收条件有多种类型,如功能性验收条件和非功能性验收条件。
      • 非功能性验收条件包括容量(capacity)、性能(performance)、可修改性(modifiability)、可用性
        (availability)、安全性(security)、易用性(usability)
8.2 为什么验收测试是至关重要的
  • 自动验收测试,总是有很多争议。因为创建和维护他们的成本太高,作者给出的经验是:通过合理地创建和维护自动验收测试套件,其成本就会远低于频繁执行手工验收和回归测试的成本,或者低于发布低质量软件带来的成本。而且自动化验收测试能捕获哪些单元或组件测试无法捕获的一些问题。
  • 手工测试通常在整个项目开发的最后,软件即将发布整个团队都面临压力的情况下进行的。可能会导致一些不可控的因素。
  • 验收测试是实际运行来证明软件能为客户提供他们所期望的业务价值。
  • 单元测试和组件测试都不测试用户场景,因此也无法发现那种用户与应用程序进行一系列交互后呈现出来的缺陷。而验收测试就是为这而设计的。
  • 验收测试有不俗的查错能力,如下:
    • 线程问题
    • 以事件驱动方式实现的应用程序出现的紧急行为
      (emergent behavior),以及由架构问题或环境及配置问题造成的其他类型的bug。这类缺陷很难通过手工测试发现,更不用说单元测试和组件测试了。
  • 放弃自动化验收测试的团队会令测试人员的负担非常重。
8.2.1 如何创建可维护的验收测试套件
  • 一旦你拿到了一些验收条件来描述用户的价值,自动化的流程如下图:
    在这里插入图片描述
  • 验验收条件像Cucumber、JBehave、Concordion、Twist
    和FitNesse这样的工具让你能够把验收条件直接写在测试中,并把它们与底层实现关联在一起。
  • 使用领域语言来实现测试是很重要的,不要把应用程序交互细节也包含在其中。直接引用程序内部的API或者UI来实现验收测试是很脆弱。这也是自动化验收被认为成本昂贵的主要原因之一。
  • 测试实现应该是通过一个较低的层次(称为应用程序驱动层)与被测试的系统进行交互。这个应用程序驱动层有一个API,它知道执行动作并返回结果个人理解:有点类似于WPF ICommand 的函数实现,驱动实现抽象到对应的Command函数就行,而不用模拟Command的具体行为,因为Command内部的行为可能会经常变,经常修改对于自动化来说代价很大,且站在用户用户价值来说,它只需要关心你的结果是否符合自动化测试的预言,而不用在意你内部的方法是否正确,你内部API或者其他函数,是单元测试来确保的是否正常的,自动化验收测试是体现用户价值。另一个方面来说,如果你程序驱动层有变化,那么自动化修改也只需要抽象到你新的API,仅仅是一个API名称的修改,代价很低
8.2.2 GUI 上的测试
  • GUI 层仅是定义为数据展现的代码,不包括任何的业务逻辑。在这种情况下,绕过界面,基于界面下的代码进行测试的风险相对小一些。
8.3 创建验收测试
8.3.1 分析人员和测试人员的角色

业务分析师
主要代表客户和系统的用户,与客户一起工作,识别需求,并排定优先级。他们(业务分析师)要与开发一起工作,确保开发人员从用户角度更好的理解需求。也要与测试人员一起工作,确保验收条件已被合理阐明。

8.3.2 迭代开发项目中的分析工作

1:分析人员定义验收条件(团队用这些验收条件来评判某个具体需求是否被满足)

	开始分析人员和测试人员和客户紧密合作,定于验收条件

2:条件定义完成,实现这个需求之前,分析、测试和开发(客户在就更好了)碰一下头

2.1:分析人员讲解需求,以及它的业务上下文,并检查一遍验收条件
2.2测试和开发讨论,并就“实现哪些自动化验收测试来证明验收条件被满足”达成一致。

这种方法可以避免分析人员创建那种难以实现或测试的“象牙塔”式需求,也避免测试人员由于自己对系统的错误理解 ,把正常的系统行为当成缺陷写在报告里,还可以避免开发人员实现一些不相干的、客户并不想要的功能。

8.3.3 将验收条件变成可执行的规格说明书
  • 自动化测试就是开发的应用程序行为的一个可执行规格说明书。这被称为行为驱动开发,行为驱动开发的核心理念之一就是验收测试应该以客户期望的应用程序行为的方式来书写。
  • 验收测试是面向业务的,所以它们应该验证应用程序的确向用户交付了价值
  • 略( 举例说明了使用工具写出了验收条件,需要下来研究这些工具,看不懂…)

总结整个过程

这种创建可执行规格说明的方法是行为驱动设计的本质。让我们再回顾一下这个
过程:
 和客户一起讨论用户故事的验收条件;
 以可执行的格式将得到的验收条件写下来;
 为这些使用领域专属语言所描述的测试写出它的代码实现,与应用程序驱动层
进行交互。
 创建应用程序驱动层,使测试通过它来与系统交互。

8.4 应用程序驱动层

应用程序驱动层所用的API是以某种领域语言表达的,可以认为是一种针对它自己的领域专属语言。
在这里插入图片描述

  • 略(给出了一个示例)表达了设计一个良好的应用程序驱动层能够提高测试的可靠性。
8.4.1 如何表述验收条件(需要研究工具)
  • JUnit和Cucumber写验收测试,它们都要比
    传统的验收测试做得好
  • 外部DSL好处:可以在验收条件之间任意切换无需用跟踪工具管理验收条件之后再用xUnit写一遍测试。
  • 如果分析人员和客户有足够的技术背景,能够使用内部DSL编写的xUnit测试的话,直接使用xUnit这种方法最好。它不太需要那些复杂的工具,只要会使用开发环境中的
    自动完成功能就可以了。你当然可以使用像AgileDox这样的工具将类名和方法名转成一个文本文档
8.4.2 窗口驱动器模式:让测试与 GUI 解耦
  • 决定验收测试需要基于GUI,应用程序驱动器层就要了解如何与其进行交互。应用程序驱动器层中与GUI交互的这部分就叫做窗口驱动器
    窗口驱动器模式
    通过提供一个抽象层减少验收测试和被测试系统GUI之间的耦合,从而让基于GUI的测试运行时更加健壮。它有助于隔离系统GUI的修改对测试的影响,如果对GUI做了一些修改,我们可以对窗口驱动器做相应的修改,这样就不用改测试了。
  • 介绍了一个工具
    • FitNesse(一个开源的测试工具)就是使用类似的方法,通过创建Fit夹具(fixture)作为你将要测试的部件的“驱动器”。在这方面,它是一个非常杰出的工具。
  • 应用程序驱动器与窗口驱动器的区别在于:窗口驱动器知道如何与GUI打交道。如果为应用程序供一个新的GUI(比如除了Web界面以外,还有一个富客户端),你只要在应用程序驱动器中再加入一个新的窗口驱动器就可以了。

在这里插入图片描述

  • 给出了2个验收机制的代码,得出了如下结论:
    1:不分层的代码少
    2:分层抽象的层次更高,可以在需要与界面交互很多不同的测试中重用这个窗口驱动器。
8.5 实现验收测试
  • 测试验收实现不仅是分层问题,还包括让应用程序达到某种特定的状态。之后在验证结果。另外还需要处理异步问题和超时问题。
  • 测试数据要细心管理,常常还需要使用替身测试,以便模拟与外部系统的集成。
8.5.1 验收测试中的状态
  • 要抵制使用生产数据的备份作为验收测试的测试数据库的诱惑,相反,我们要维护一个受控的数据最小集
  • 验收测试最有效的方法是:利用应用程序自身的功能特性来隔离测试的范围比
    如,软件支持多个具有独立账户的用户,就可以用这个功能特性在每个测试开始之前都创建一个新用户.
  • 测试用例保证初始状态而且运行后清理干净
8.5.2 过程边界、封装和测试
8.5.3 管理异步与超时问题
  • 应该避免所有异步情况,也要避免跨越测试边界的情况,不然你会遇到难以发现的偶发性测试失败。
  • 略过(遵循上面原则会减少遇到很多问题)
8.5.4 使用测试替身对象
  • 自动化验收测试不应该运行在包含所有外部系统集成点的环境中。相反,应该为自动化验收测试提供一个受控环境,并且被测系统应该能在这个环境上运行
  • 在做验收测试时,应该最小化外部依赖的影响
  • 创建测试替身对象,用于代表系统与所有外部系统交互的连接器.给出了一张图
    在这里插入图片描述
  • 用替身对象取代外部系统还有一个好处,那就是能够控制行为、模拟通信失败、模拟错误响应事件或高负载下的响应等,所有这些都能在我们的掌握之中。

测试外部集成点

  • 外部集成点通常是很多问题的来源。外系统共享的数据结构、消息交换的频率以及寻址机制相关配置的修改都可能会引起问题,以及外部系统修改了代码也可能会导致问题。
  • 测试需要注意这些可能出现问题的点,如果两个系统都在开发当中需要定期做仔细的测试,来识别两个系统的临界点。
8.6 验收测试阶段

提交测试一旦成功完成,就应该开始在通过提交测试的软件版本上运行验收测试套件。

  • 验收测试失败的构建版本不能被部署
  • 通过验收测试过后才能作为候发布版本走向最后阶段。
  • 对于验收测试,不应该提供这种人为评定的机会,如果成功,就可以继续,如果失败,就不能向前。
  • 反复执行验收测试,是一个成本投资。会节约很多倍的维护成本。当对应用程序大概时,它就是一张防护网(减少BUG产生的防护网)
  • 自动化UI测试的过程中,可以使用屏幕录制软件作为排查问题的”素材"
8.6.1 确保验收测试一直处于通过状态
  • 开发人员必须要确保,验收测试通过。就像等待提交测试那样。
8.6.2 部署测试
  • 运行验收测试时,我们设计的测试环境会尽可能与期望的生产环境一致。如果
    成本不太高的话,它们就应该是一样的。
  • 其他略过,实际工作中不会设计到
8.7 验收测试的性能

*自动化验收测试性能并不是主要考虑的问题。

8.7.1 重构通用任务
  • 最显而易见且快速奏效的方法就是每次构建结束后都找到最慢的几个测试
8.7.2 共享昂贵资源
  • 在某个测试开始之前,创建一个标准的空白的应用程序实例,并在它结束之后,把这个实例销毁。
  • 我们要找出测试间会共享哪些资源,以及哪些资源要被单个测试独占。通常,对于大多数基于服务器的应用程序来说,都可以共享这个服务器的同一个实例。在执行验收测试前,创建一个干净的系统运行实例用于测试,在这个实例上运行所有的验收测试,最后再将它关闭。
8.7.3 并行测试
  • 对于独立性比较好时,那些基于服务器的多用户系统来说,并行显而易见。
8.7.4 使用计算网格
  • 昂贵的测试/或者那些需要模拟并发用户的测试使用计算网格的益处非常大。
8.8 小 结
  • 使用验收测试对提高开发流程的效率非常重要
  • 自动化验收测试通常要比单元测试复杂,需要更多的时间进行维护
  • 由于它在修复某个失败与使所有验收测试套件成功通过之间那种固有的滞后性,所以与单元测试相比,它处于失败状态的时间要长一些。
  • 手工测试有其自己的位置,如探索性测试、易用性测试和用户验收测试和演示
  • 单元测试的关注点并不是业务本身
验收测试条件驱动的测试代表了更先进的理念,因为它:

 为“软件是否满足业务目标”提供了更高的信心;
 为系统进行大范围修改提供了一个保护网;
 通过全面的自动回归测试极大地提高了质量;
 无论什么时候出现缺陷,都能提供快速、可靠的反馈,以便可以立即修复;
 让测试人员有更多的时间和精力去思考测试策略、开发可执行的规格说明,以
及执行探索性测试和易用性测试;
 缩短周期时间,使持续部署成为可能。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值