31 程序员怎么学习运维知识?

在上一讲中,我们讲到了开发过程的自动化,我们的关注点在于如何构建出一个有效的部署包,这个包最终是要上线部署的,那接下来,我们就来关心一下部署的相关工作。

零散的运维知识

在一些稍具规模的公司,为部署工作设置了一个专有职位,称之为运维。

当然,这个岗位的职责远不止部署这一件事,还要维护线上系统的稳定。

不过,如果你的团队规模不大,或是项目处于初始阶段,这些工作往往也要由程序员自行完成。

对于一个程序员来说,了解自己的程序怎么部署上线,是非常重要的。

我们既要了解一个软件的逻辑,也要知道它的物理部署。只有这样,出了问题才知道怎么修复

更重要的是,我们在设计时,才能尽量规避部署带来的问题。

而部署,恰恰也是最适合发挥自动化本领的地方。

好,即便下定决心准备学习运维相关知识,你准备怎么学呢?

我先来问你个问题,提到运维,你会想到什么?

如果你是一个刚刚步入这个行业的程序员,你或许会想到 Docker,想到 Kubernetes;如果再早一点入行,你或许还会想到 Chef、Puppet、Ansible;更早一些入行的话,你会想到 Shell 脚本。

没错,这些东西都是与运维相关的。那我就这么一个一个地都学一遍吗?

就我个人的学习经验而言,如果所有的知识都是零散的,没有一个体系将它们贯穿起来,你原有的知识无法帮助你学习新知识,这种学习方式效率极低,过程也极其痛苦。

如果是有结构的知识,所谓的学习新知识不过是在学习增量,真正要理解的新东西并不多,学习效率自然会大幅度提高。

所以,想学好运维知识,首先你要建立起一个有效的知识体系

你可能会问,这些运维知识看上去就是一个一个独立的工具啊?

我曾经也为此困惑了许久,虽然我对各个工具已经有了不少的了解,但依然缺乏一个有效的知识体系,将它们贯穿起来,直到我上了一堂课。

感谢 Odd-e 的柴锋,有一次,他给我上了一堂 DevOps 课,他对运维知识的讲解让我茅塞顿开,从此,我的运维知识有了体系。

准确地说,他的这堂课就是讲给程序员的运维课。

今天,我就把这个体系按照我的理解,重新整理一遍分享给你,也算是完成一次知识输出。

Java 知识体系

正如我前面所说,学习一个新东西,最好的办法是学习增量,如果能够找到它与已有知识体系的联系,我们就可以把已有知识的理解方式借鉴过去。

作为程序员,我们其实已经有了一个完善的知识体系,这就是我们对于程序设计的理解,而理解运维的知识体系,刚好可以借鉴这个体系。怎么理解这句话呢?

以最常见的 Java 开发为例,如果要成为一个合格的 Java 程序员,我应该知道些什么呢?

首先肯定是 Java 语言,我需要了解 Java 语言的各种语法特性。不过,只了解语法是写不出什么像样程序的,我们还需要掌握核心库。

对于 Java 来说,就是 JDK 中的各种类,比如,最常见的 String、List、Map 等等。

理论上来说,掌握了基本的语法和核心库,你就可以开发任何程序了。但在实践中,为了避免重新发明“轮子”,减少不必要的工作量,我们还会用到大量的第三方类库,比如,Google Guava、SLF4J 等等。

除了功能实现,还有一些结构性的代码也会反复出现。比如说,在常见的 REST 服务中,我们要将数据库表和对象映射到一起,要将结果转换成 JSON,要将系统各个组件组装到一起。

为了减少结构上的代码重复,于是,开发框架出现了,在 Java 中最常见的开发框架就是 Spring。

至此,你就可以完成基本的代码编写,但这还不够。

在 Java 中,你不会从底层完成所有事情,比如,虽然你写 REST 服务,但你很少会接触到最底层的 HTTP 实现,因为这些工作由运行时环境承担了。

我们要做的只是把打好的包部署到这些运行时环境上,在 Java 的世界里,这是 Tomcat、Jetty 之类的容器承担的职责。

如果你刚刚加入这一行,上来就用 Spring Boot 之类的框架写代码,你可能并没有碰到这样的部署过程,因为这些框架已经把容器封装其中,简化了部署过程。

Tomcat、Jetty 往往还只是在一台机器上部署,在现实的场景中,一台机器通常是不够用的,我们可能需要的是一个集群。

你可能会想到用 Nginx 来做一个负载均衡,但如果用原生的 Java 解决方案,这时候就轮到企业级的应用服务器登场了,比如:IBM WebSphere、Oracle WebLogic Server、JBoss Enterprise Application Platform 等等。

至此,一套完整的 Java 应用解决方案已经部署起来了。

但我们知道了这些,和我们运维知识有什么关系呢?我们可以用同样的体系去理解运维知识。

运维知识体系

首先,要理解运维体系的语言。

运维的语言是什么呢?是 Shell,人们最熟悉的应该是 Bash。

我们通过操作系统与计算机打交道,但我们无法直接使用操作系统内核,Shell 为我们提供了一个接口,让我们可以访问操作系统内核提供的服务。

你可能会以为我这里用的是比喻,将 Shell 比喻成语言,但还真不是,Shell 本身就是一门编程语言。

绝大多数人都知道 Shell 可以编程,但几乎没有人把 Shell 当成一门编程语言来学习,基本上都是在需要的时候,搜索一下,然后照猫画虎地将代码复制上去。

这样造成的结果就是,一旦写一个脚本,就要花费大量的时间与语法做斗争,只是为了它能够运行起来。

有了语言,再来就是核心库了。

运维的核心库是什么?就是 Shell 提供的各种 Unix/Linux 的核心命令,比如:ls、cd、ps、grep、kill、cut、sort、uniq 等等,它们几乎与操作系统绑定在一起,随着操作系统一起发布。

了解了核心的部分,还需要了解一些第三方库,运维知识的第三方库就是那些不属于操作系统核心命令的命令,比如:rsync、curl 等等

Java 有框架可用,运维也有框架吗?

你可以想一下,Java 的框架提供的是一些通用的能力,在运维工作中,也是有一些通用能力的,比如:在安装某个包之前,要检查一下这个包是否已经安装了;在启动一个服务前,要检查这个服务是否启动了,等等。所以,能够帮我们把这些工作做好的工具,就是我们的运维框架。

到这里,你应该已经明白了,我在说的运维框架其实就是像 Chef、Puppet、Ansible 之类的配置管理工具。它们做的事就是把那些繁琐的工作按照我们的定义帮我们做好。

有了对软件环境的基本配置,接下来,就要找一个运行时的环境将软件跑起来了。这时候,我们要了解像虚拟机、Docker 之类的技术,它们帮我们解决的问题就是在单机上的部署

一般来说,了解了这些内容,我们就可以构建出一个开发环境或测试环境。除非用户非常少,我们可以在生产环境考虑单机部署,否则,我们迄今为止讨论的各种技术还都是在开发环节的。

如果我们需要一个集群或是高可用环境,我们还需要进一步了解其他技术,这时候,就轮到一些更复杂的技术登场了,比如,云技术,Amazon AWS、OpenStack,包括国内的阿里云。

如果你采用的是 Docker 这样的基础技术,就需要 Kubernetes、Docker Swarm 之类的技术

至此,一个相对完整的运维知识体系已经建立起来了,现在你有了一张知识地图,走在运维大陆上,应该不会轻易地迷失了。

希望你可以拿着它,继续不断地开疆拓土。

总结时刻

我们今天的关注点在于,将开发过程产生的构建产物部署起来。部署过程要依赖于运维知识,每个程序员都应该学习运维知识,保证我们对软件的运行有更清楚地认识,而且部署工作是非常适合自动化的。

但是,对运维工具的学习是非常困难的,因为我们遇到的很多工具是非常零散的,缺乏体系。

这里,我给你介绍了一个运维的知识体系,这个体系借鉴自 Java 的知识体系,包括了编程语言、核心库、第三方库、开发框架、单机部署和集群部署等诸多方面。

我把今天提到的各种技术整理成一个表格列在下面,你可以参考它更好地理解运维知识。

compare

如果今天的内容你只能记住一件事,那请记住:有体系地学习运维知识

最后,我想请你分享一下,你还能想到哪些运维知识可以放到这张知识地图上呢?欢迎在留言区写下你的想法。

感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。

32 持续交付:有持续集成就够了吗?

在前面两讲,我给你讲了开发过程的自动化,将我们的程序打成发布包;然后讲了部署过程的自动化,通过各种工具将发布包部署起来。

有了这些基础,我们就可以考虑在每次开发完之后,将程序打包部署到环境中。开发完就自动打包,然后自动部署,听起来很像持续集成是不是?

关于持续集成,我在专栏里已经讲过两次,分别讨论了“为什么要做持续集成”和“怎么做好持续集成”。但持续集成的讨论只停留在开发环节。

有了前面两讲的准备,我们就可以把这个过程再进一步延伸。聪明的你或许已经听出来了,这次我要讲的主题是持续交付

持续交付

让持续交付这个概念广为人知的是一本书,Jez Humble 和 Dave Farley 的《持续交付》(Continuous Delivery)。

前面讲持续集成的发展历史时,我提到了 CruiseControl,它是持续集成服务器的鼻祖。

因为持续集成的不断发展,2007年,我的老东家 ThoughtWorks 公司有意以 CruiseControl 为基础,提供企业级服务,于是成立了一个团队,打造一个更好的持续集成服务器,Jez Humble 就是在这个团队中工作的。

同样在这个团队工作的还有一个人,乔梁,他是《持续交付》这本书的中文版译者,而且在这本书出版近十年后,他自己写了《持续交付 2.0》,把自己多年来关于持续交付的新理解整理了进去。

那么,什么叫更好的持续集成服务器呢?当时我的理解很浅薄,只是希望它有更好的界面,更快的构建速度,而 Jez Humble 他们对于这个产品的构想远远超过了我当时的想象,他们将生产环境也纳入了考量。

什么是持续交付?简言之,它就是一种让软件随时处于可以部署到生产环境的能力。

从一个打好的发布包到部署到生产环境可用,这中间还差了什么呢?那就是验证发布包,部署到环境中

验证发布包,你或许会想,这不是测试的事吗?这不是已经在持续集成阶段完成的吗?不尽然。在持续集成阶段验证的包,往往缺少了环境的支持。

因为持续集成的环境往往是单机的,主要强调功能验证,而一些与生产环境相关的测试往往是欠缺的。

所以,这里就引出了持续交付中一个需要关注的点:环境。

一般来说,在构建持续交付的基础设施时,会有下面几个不同的环境。

持续集成环境,持续集成是持续交付的前提,这个过程主要是执行基本的检查,打出一个可以发布的包。

测试环境(Test),这个环境往往是单机的,主要负责功能验证,这里运行的测试基本上都是验收测试级别的,而一般把单元测试和集成测试等执行比较快的测试放到持续集成环境中执行。

预生产环境(Staging),这个环境通常与生产环境配置是相同的,比如,负载均衡,集群之类的都要有,只是机器数量上会少一些,主要负责验证部署环境,比如,可以用来发现由多机并发带来的一些问题。

生产环境(Production),这就是真实的线上环境了。

持续集成

持续交付

你也看出来了,每个环境的作用是有差异的,所以,通常不会将所有的验证放在一起执行,而是要分阶段的去执行,一个阶段不通过,是不能进入下一阶段的,这种按照不同阶段组织构建的方式,称之为构建流水线(Build Pipeline)。

一旦通过了各种验证,就会到构建流水线的最后一个阶段,生产发布。

通常来说,生产发布这个过程不是自动化的。

我们说,持续交付的关注点在于,让软件具备随时可以发布的能力,但并不等于它要立刻上线,所以,最后这一下,还要由人来决定,到底是不是要上线。

如果把由人决定的是否上线变成自动化的,就成了另外一个实践:持续部署。

但通常人们都会比较谨慎,最后这一下还是由人拍板比较稳妥,所以,持续交付是现在的主流。

持续部署

至此,我们讨论了持续交付的第一个方面,验证发布包。接下来,我们再来看看另外一个重要部分:部署。

DevOps

早期人们做部署都是自己编写 Shell 脚本完成的,但在上一讲中,我提到的一些工具,比如:Chef、Puppet、Ansible 等等,大幅度地简化了部署脚本的编写。

这些工具在业界的兴起与一个概念息息相关:DevOps。

DevOps 是一种软件交付的理念和方法,目的是增强软件的可靠性。从名字便不难发现,DevOps 是将开发(Development)和运维(Operations)组合在了一起。

在传统的 IT 公司中,开发和运维往往是井水不犯河水的两个职位,甚至是两个不同的部门,由此带来了很多问题,比如,开发人员修改了配置,但没有通知运维,造成了新代码不能运行。

DevOps 提倡的就是将二者融合起来,打破壁垒。2009年,Flickr 做了一个分享《每天部署10次》,整个行业受到了极大的冲击,从此 DevOps 运动风起云涌。

DevOps 给这个行业带来的理念冲击是很大的,想要做好 DevOps,需要在文化、流程和工具等诸多方面不断改善。

但对我们程序员的日常工作来说,最直接的影响是体现在各种工具上。

Chef、Puppet、Ansible 这些工具基本上都是在那之后,兴起或广为人知的。

在上一讲中,我给你讲了这些配置管理工具在运维体系中的角色,它们相当于提供了一个框架。但对于行业来说,这些工具给行业带来了部署的规范。

从前写 Shell 的方式,那就是各村有各村的高招。

你在 A 公司学会的东西,到 B 公司是没法用的,甚至在很多人的印象中,部署这件事就应该属于某个特定的场景,换台机器脚本都要重新写过。

这种形势就如同 Spring 出现之前,几乎所有的公司都在写自己的框架一样。

Spring 的出现打破这一切,让你的 Java 技能从归属于一个公司变成了行业通用。同样,运维体系中这些配置工具也起到了这样的作用。

它们甚至带来了一个新的理念:基础设施即代码(Infrastructure as code),将计算机的管理与配置变成了代码

一旦成了代码,就可以到处运行,可以版本管理,那种强烈依赖于“英雄”的机器配置工作终于可以平民化了。

这在从前是想都不敢想的事。

这些工具采用的都是声明式接口,从 Shell 那种描述怎么做,到描述做什么,抽象程度上了一个台阶,让开发者或系统管理员从琐碎的细节中脱身,把更多的注意力用于思考应该把机器配置成什么样子。

如果这些配置管理工具还需要有一台具体的机器去部署,放在持续交付中,也只能扮演一个部署环境的次要角色,那 Docker 的出现则彻底地改变最终交付物。

我在上一讲说过,Docker 相当于是一台机器。

Docker 非常好的一点是,它是一台可以用代码描述的机器,在 Docker 配置文件中描述的就是我们预期中那台机器的样子,然后,生成镜像,部署到具体的机器上。

既然是要描述机器的样子,我们就可以在 Docker 的配置文件中使用前面提到的配置工具,如此一来,我们的配置工作就简单了。

那既然我们在讨论持续交付,还可以通过配置工具将我们的发布包也部署到最终的镜像中。

这样一来,最终生成的镜像就是包含了我们自己应用的镜像。

你或许已经知道我要说什么了,结合着这些工具,我们的生成产物就由一个发布包变成了一个 Docker 镜像。

docker 镜像

Docker 在开发中扮演的角色,是一个构建在我们应用与具体机器之间的中间层。

对应用而言,它就是机器,但对机器而言,它只是一个可以部署的镜像,统一了各种应用千奇百怪的部署差异,让部署本身变得更简单了。

到这里,我给你介绍了持续交付中最基础的东西,让你有了一个基本的框架理解持续交付。

当然,如果你关注这个领域,就会发现,它早已超出了一个实践的层面,有更多组织、文化的内容。

Jez Humble 写《持续交付》时就已经想到如此完整的一个体系,受限于当时的环境,书中介绍的自动化还比较宽泛,不像今天有更加完善的工具支撑。

只可惜,虽然当时他对持续交付的理解已经到达如此高度,他所在的团队也做出了一个颇具先锋气质的持续交付工具,但是受限于产品推广策略,这个工具并没有成为主流,即便后来开源了。

(如果你想了解一下这个工具,可以点击链接去查看)

总结时刻

总结一下今天的内容。我们延续了前两讲的内容,在准备好发布包和部署的基础设施之后,我们顺着持续集成的思路,将部署过程也加了进来,这就是持续交付。

持续交付,是一种让软件随时处于可以部署到生产环境的能力。

让软件具备部署到生产环境的能力,这里面有两个关键点:验证发布包和部署

验证发布包,不仅是功能上的验证,还包括与环境结合在一起的验证。

所以,通常会用几个不同的环境验证,每一个环境都是一个单独的阶段,一个阶段不通过,是不能进入下一阶段的,这种按照不同阶段组织构建的方式,称之为构建流水线(Build Pipeline)。

与部署相关的一个重要概念是 DevOps,也就是将开发和运维结合起来。DevOps 包含了很多方面,对程序员最直接的影响是各种工具的发展,这些工具推动着另一个理念的发展:基础设施即代码(Infrastructure as code) 。

有赖于这些工具的发展,今天定义交付,就不再是一个发布包,而是一个可以部署的镜像

如果今天的内容你只能记住一件事,那请记住:将部署纳入开发的考量。

最后,我想请你分享一下,你对持续交付的理解是什么样的呢?欢迎在留言区写下你的想法。

33 如何做好验收测试?

经过前面三讲的讲解,相信你对一个项目自动化应该是什么样子有了一个相对完整的认识:程序员写好程序,用构建脚本执行检查,提交代码,在服务器上打出一个发布镜像,部署到各个环境进行检查,检查好了,随时可以发布上线。

我们在前面的内容中只说了该检查,但怎么检查呢?

这就轮到测试发挥作用了。

在“任务分解”的模块,我给你完整地介绍了一下开发者测试的概念,但在那个部分讲解的测试基本上还停留在单元测试和集成测试的范畴。

对于整个应用该怎么测,我们并没有仔细讨论。

今天我们就来说说应用测试的话题:验收测试。

验收测试

验收测试(Acceptance Testing),是确认应用是否满足设计规范的测试。这种测试往往是站在用户的角度,看整个应用能否满足业务需求。

从名字上来看,验收应该是业务人员的事,但业务人员能做的最多只是验收,测试是他们无论如何也不太可能做仔细的。

所以,验收测试这件事,往往还是由技术团队自己完成,而且在很多公司,这就是测试人员的事。

时至今日,很多测试团队都拥有自动化的能力。所以,自动化验收测试自然是重点考虑对象。今天,我们的重点就是怎么做好自动化的验收测试。

其实,验收测试应该是人们最早想到的自动化测试,早在单元测试还不流行的年代,人们就开始了对自动化验收测试的探索。有不少团队甚至还构建了自己的框架,只不过,这种框架不是我们今天理解的测试框架,而是针对着一个应用的测试框架。

比如,我曾经见过有人为通信软件构建的一套完整的测试框架,甚至构建了属于自己的语言,测试人员的工作就是用这种特定的语言,对系统进行设置、运行,看它是否满足自己的预期。

相对来说,他们的这种做法已经非常成熟了。但更多团队的现实情况是,自己把对应用的访问做一个简单的封装,然后,写测试就是编写代码调用这个封装。

让验收测试从各自为战的混乱中逐渐有了体系的是行为驱动开发(Behavior Driven Development)这个概念的诞生,也就是很多人知道的 BDD

行为驱动开发

行为驱动开发中的行为,指的是业务行为。

BDD 希望促进业务人员与开发团队之间的协作,换句话说,如果你想做 BDD,就应该用业务语言进行描述。

这与我们传统上理解的系统测试有着很大的差别,传统意义上的系统测试是站在开发团队的角度,所以,更多的是在描述系统与外部系统之间的交互,用的都是计算机的术语。

而 BDD 则让我们换了一个视角,用业务语言做系统测试,所以,它是一个更高级别的抽象。

BDD 是2003年由 Dan North 提出了来的。

Dan North 不仅仅提出了概念,为了践行他的想法,他还创造了第一个 BDD 的框架:JBehave。

后来又改写出基于 Ruby 的版本 RBehave,这个项目后来被并到 RSpec 中。

今天最流行的 BDD 框架应该是 Cucumber,它的作者就是 RSpec 的作者之一 Aslak Hellesøy。

Cucunber 从最开始的 Ruby BDD 框架发展成今天支持很多不同程序设计语言的 BDD 测试框架,比如,常见的 Java、JavaScript、PHP 等等。

BDD 框架给我们最直观的感受就是它给我们提供的一套语言体系,供我们描述应用的行为,下面是一个例子,它描述了一个交易场景,应用需要根据交易结果判定是否要发出警告。

你可以感受一下:

Scenario:  trader is not alerted below threshold
 
Given a stock of symbol STK1 and a threshold of 10.0
When the stock is traded at 5.0
Then the alert status should be OFF

Scenario:  trader is alerted above threshold
 
Given a stock of symbol STK1 and a threshold of 10.0
When the stock is traded at 11.0
Then the alert status should be ON

我们在这里的关注点是这个例子的样子,首先是描述格式:“Given…When…Then”,这个结构对应着这个测试用例中的执行步骤。

Given 表示的一个假设前提,When 表示具体的操作,Then 则对应着这个用例要验证的结果。

还记得我们讲过的测试结构吗?前置准备、执行、断言和清理,这刚好与“Given…When…Then”做一个对应,Given 对应前置条件,When 对应执行,Then 则对应着断言。

至于清理,它会做一些资源释放,属于实现层面的内容,在业务层面上意义不大。

了解了格式,我们还要关心一下内容。你会看到这里描述的行为都是站在业务的角度进行叙述的,而且 Given、When、Then 都是独立的,可以自由组合。

也就是说,一旦基础框架搭好了,我们就可以用这些组成块来编写新的测试用例,甚至可以不需要技术人员参与。

不过,这些内容都是站在业务角度的描述,没有任何实现的内容,那实现的内容放在哪呢?

我们还需要定义一个胶水层,把测试用例与实现联系起来的胶水层,在 Cucumber 的术语里,称之为步骤定义(Step Definition)。

这里我也给出了一个例子,你可以参考一下:

public class TraderSteps implements En {
    private Stock stock;

    public TraderSteps() {
        Given("^a stock of symbol {string} and a threshold of {double}", (String symbol, double threshold) -> {
            stock = new Stock(symbol, threshold);
        });

        When("^the stock is traded at {double}$", (double price) -> {
            stock.tradeAt(price);
        });

        Then("the alert status should be {string}", (String status) -> {
            assertThat(stock.getStatus().name()).isEqualTo(status);
        })
    }
}

写好验收测试用例

有了对 BDD 框架的基本了解,接下来的问题就是,怎么用好 BDD 框架。

我们举个简单的例子,如果我们要写一个登录的测试用例,你会怎么写呢?

有一种写法是这样的,为了方便叙述,我把它转成了中文描述的格式,Cucumber 本身是支持本地化的,你可以使用自己熟悉的语言编写用例:

假定 张三是一个注册用户,其用户名密码分别是 zhangsan 和 zspassword
当 在用户名输入框里输入 zhangsan,在密码输入框里输入 zspassword
并且 点击登录
那么 张三将登录成功

这个用例怎么样呢?或许你会说,这个用例挺好的。如果你这么想,说明你是站在程序员的视角。

我在前面已经说过了,BDD 需要站在业务的角度,而这个例子完全是站在实现的角度。

如果登录方式有所调整,用户输完用户名密码自动登录,不需要点击,那这个用例是不是需要改呢?

下面我换了一种方式描述,你再感受一下:

假定 张三是一个注册用户,其用户名密码是分别是 zhangsan 和 zspassword
当 用户以用户名 zhangsan 和密码 zspassword 登录
那么 张三将登录成功

这是一个站在业务视角的描述,除非做业务的调整,不用用户名密码登录了,否则,这个用例不需要改变,即便实现的具体方式调整了,需要改变的也是具体的步骤定义。

所以,想写好 BDD 的测试用例,关键点在用业务视角描述

编写验收测试用例的步骤定义时,还有一个人们经常忽略的点:业务测试的模型。

很多人的第一直觉是,一个测试要啥模型?

还记得我们讲好测试应该具备的属性吗?

其中一点就是 Professional,专业性。

想要写好测试,同写好代码是一样的,一个好的模型是不可或缺的。

这方面一个可以参考的例子是,做 Web 测试常用的一个模型:Page Object。它把对页面的访问封装了起来,即便你在写的是步骤定义,你也不应该在代码中直接操作 HTML 元素,而是应该访问不同的页面对象。

以前面的登录为例,我们可能会定义这样的页面对象:

public class LoginPage {
    public boolean login(String name, String password) {
      ...
    }
}

如此一来,在步骤定义中,你就不必关心具体怎么定位到输入框,会让代码的抽象程度得到提升。

当然,这只是一个参考,面对你自己的应用时,你要考虑构建自己的业务测试模型。

总结时刻

今天我和你分享了自动化验收测试的话题。

验收测试(Acceptance Testing),是确认应用是否满足设计规范的测试。

验收测试是技术交付必经的环节,只不过,各个团队实践水平有所差异,有的靠人工,有的用简单自动化,一些做得比较好的团队才有完善的自动化。

自动化验收测试也是一个逐步发展的过程,从最开始的各自为战,到后来逐渐形成了一个完整的自动化验收测试的体系。

今天,我以行为驱动开发(Behavior Driven Development,BDD)为核心,给你介绍了一种自动化验收测试的方式。这个在2003年由 Dan North 提出的概念已经成为了一套比较完善的体系,尤其是一些 BDD 框架的发展,让人们可以自己的项目中实践 BDD。

我以 Cucumber 为样例,给你介绍了 BDD 验收用例的编写方式,你知道“Given…When…Then”的基本格式,也知道了要编写步骤定义(Step Definition)将测试用例与实现连接起来。

我还给你介绍了编写 BDD 测试用例的最佳实践:用业务的视角描述测试用例。在编写步骤定义时,还要考虑设计自己的业务测试模型。

其实,验收测试的方法不止 BDD 一种,像实例化需求(Specification by Example,SbE)也是一种常见的方法。

验收测试框架也不止 BDD 框架一类,像 Concordion 这样的工具甚至可以让你把一个验收用例写成一个完整的参考文档。

如果你有兴趣,可以深入地去了解。无论哪种做法,都是为了缩短业务人员与开发团队之间的距离,让开发变得更加高效。

如果今天的内容你只能记住一件事,那请记住:将验收测试自动化。

最后,我想请你分享一下,你的团队是怎么做验收测试的呢?欢迎在留言区分享你的做法。

参考资料

http://learn.lianglianglee.com/%e4%b8%93%e6%a0%8f/10x%e7%a8%8b%e5%ba%8f%e5%91%98%e5%b7%a5%e4%bd%9c%e6%b3%95/31%20%e7%a8%8b%e5%ba%8f%e5%91%98%e6%80%8e%e4%b9%88%e5%ad%a6%e4%b9%a0%e8%bf%90%e7%bb%b4%e7%9f%a5%e8%af%86%ef%bc%9f.md