从零开始掌握微服务软件测试_第1页
从零开始掌握微服务软件测试_第2页
从零开始掌握微服务软件测试_第3页
从零开始掌握微服务软件测试_第4页
从零开始掌握微服务软件测试_第5页
已阅读5页,还剩11页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、从零开始掌握微服务软件测试什么是微服务?微服务的由来微服务的前身是Peter Rodgers博士在2005年度云端运算博览会上提出的微 Web月艮务(Micro-Web-Service)。微软的Juval L?wy随后也提出了类似的想法,并 提议将其作为微软下一阶段最主要的软件架构。2014年,Martin Fowler与James Lewis共同提出了微服务的概念,给出了微服务 的具体定义:从本质上来说,微服务是一种架构模式。它是面向服务型架构(SOA)的一 种变体,提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用 户提供最终价值。每个服务运行在其独立的进程中,服务与服

2、务之间采用轻量级的通信机 制互相沟通(通常是基于HTTP的RESTful API)。每个服务都围绕着具体业务进行构 建,并且能够被独立地部署到生产环境、类生产环境等。另外,应尽量避免统一的、集中 式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具 对其进行构建。Martin Fowler是国际著名的软件专家,敏捷开发方法的创始人之一,现为 ThoughtWorks公司的首席科学家。在面向对象分析设计、UML、模式、软件开发方法 学、XP、重构等方面,都扮演着举足轻重的开创者角色。早在20世纪80年代,Fowler 就是使用对象技术构建多层企业应用的倡导者,他著有几本

3、经典书籍:企业应用架构模 式、UML精粹和重构等。微服务与传统开发方式的区别与微服务架构相对应,传统开发方式通常被称为单体式架构(Monolithic Architecture)。所有功能都打包在一起,基本没有外部依赖,其中包含了数据输入输出、数据处理、业务实现、错误处理、前端显示等所有逻辑。卜图显示的一个典型的单体式架构示意图:这种架构有其优点,包括:开发团队的组织架构简单,便于集中式管理。因为开发进度统一管理,避免重复开发的问题。所有功能都集中在本地,不存在分布式的管理和调用损耗。但是,随着现代应用程序的日益复杂化,加上对于迭代速度的要求越来越高,这种架 构的不足开始暴露出来:效率低:所有

4、开发人员都在同一个项目下修改代码,经常需要相互等待对方的功能更 新,代码入库时的冲突不断,造成极高的开发成本。维护难:各个模块的代码都耦合在一起,一方面新人不知道从何下手,一方面一旦出 现问题(Bug),就需要大改。在某个模块需要升级时,也不得不升级整个应用程序。不灵活:构建(Build)时间过长,任何一个小量级的修改,都要重构整个项目,非常 耗时。稳定性差:一个微小的问题,都可能导致整个进程崩溃,使得整个应用程序无法工 作。扩展性不够:难以分布式部署和扩容,无法满足高并发下的业务需求。而且,一旦业 务范围扩展或者需求有所变化,难以复用原有的服务,必须重新开发。如何解决这些问题?微服务架构逐渐

5、浮出水面。从软件开发的组织上来说,它的核心 理念是按照业务边界把整个系统划分为若干个子系统。每个子系统的开发团队之间, 保持着合作(Inter-Operate)而不是整合(Intergrate)的关系。定义好每个子系统的边 界和接口,在一个团队内自治。团队按照这样的方式组建,沟通的成本维持在系统内部, 每个子系统就会更加内聚,彼此的依赖耦合能变弱,跨系统的沟通成本也就能降低。这里不得不提及著名的康威定律”(Conways Law),这是微服务架构的一个核 心理念。Melvin Conway 在 1967 年提出了这个理念,原文是:Organizations which design syste

6、ms are constrained to produce designs which are copies of the communication structures of these organizations.用简单的话来说,就是组织形式等同于系统设计,组织的沟通方式会通过系统设计表 现出来。下面这幅著名的软件企业组织图,与这些企业的产品架构有着异曲同工的对应关 系。再以上面提到的单体式App为例,通过用微服务架构方式对其进行改造,将会变成 下面这种结构:除了解决单体式架构的几个缺陷以外,微服务架构还具有下面这些优点:部署、回滚变得更快、更简便。微服务架构中,提倡针对不同的业务特征选

7、择合适的技术方案,有针对性的解决具体 业务问题,而不是像单块架构中采用统一的平台或技术来解决所有问题。这样就实现了技 术的多元化,无需长时间锁定于某一种技术栈,便于采用最新的工具。每个服务都可以单独扩容。在需要发布新功能时,可以用插件的形式添加到系统中而不需要重新部署整个系统。微服务架构提供自主管理其相关的业务数据,这样可以随着业务的发展提供数据接口 集成,而不是以数据库的方式同其他服务集成。另外,随着业务的发展,可以方便地选择 更合适的工具管理或者迁移业务数据。当然也需要提到,微服务架构也存在着它的不足:由于把每个子系统分配各不同的团队,这不仅意味着系统内部通信需求的增加,也带 来了不同团队

8、之间交流成本的提高。在对基于微服务架构的分布式系统进行测试时,复杂度会大幅度提高。分布式部署会给团队的DevOps能力提出更高的要求。当服务数量增加时,管理的复杂度也会指数级增加。DevOps( Development和Operations的组合词)是一种重视软件开发人员和IT 运维技术人员之间沟通合作的文化、流程或者实践方式。透过完全自动的软件交付”和 架构变更流程,使得构建、测试、发布软件更加快捷、频繁和可靠。微服务架构对测试人员意味着什么?介绍完微服务架构以后,回到主题上来:对于测试人员而言,微服务架构到底有什么 特点呢?我把它归结为以下几点:每个服务承担一定的职责:尽可能小,但是又达到

9、必要的规模(as small as possible but as big as necessary)。在问答网站Quora上,有一个著名的问题:什么是程序员觉得最浪费时间的事情? 排名第一的回答中提到:不必要的微服务。这句话揭示了开发团队在转向微服务架构时经常走入的误区。微固然重要,但是 首要的是提供服务,这才构成微服务的价值。盲目地切分功能(Feature),却 没有起到解耦合的作用,只是会增加维护、测试的成本。毕竟,多一项服务,就会多出一 系列的流水线和测试要求。因此,测试、质量人员在面临团队计划采取微服务架构的决策 时,必须要敢于质疑:是否有这样做的必要?目的是让决策人员意识到这种转型

10、的潜在成 本,避免花无用功。微服务之间通常通过Rest over HTTP连接。最常见的连接/交互方式,即通过POST、GET、PUT、DELETE这些命令操作API, 通过JSON传递参数。以下面这个典型的制造型企业的运营系统为例。在从单体式架构 转为微服务之后,不同功能模块之间将通过Rest方式互相访问。这种简易、明确的交互方式为契约测试(Contract Test)提供了基础。每种服务不一定提供用户界面。这意味着每种服务的测试,并不一定能够或者需要从UI完成。这对API级别的集 成化测试提出了要求。微服务通常还可以划分为更小的模块。如下图所示,一个典型的微服务可以分为这几个模块:资源、业

11、务逻辑、数据存储接 口、外部通信接口等。这意味着,在对微服务架构进行测试时,可以从不同的模块着手,进行相应的模块测 试。总结简单总结一下所学习的内容:微服务架构是针对单体式架构的不足,随着应用程序复杂度的增加、部署频率加快的 要求,应运而生。微服务架构带来了简化部署、隔离功能/缺陷、便于升级/扩容等优点,但也具有提高 交流成本、增加测试复杂度等不足。对于测试人员,微服务架构具备一些特别需要注意的特征,要求采用不同的测试方法 加以应对。微服务对软件测试提出的挑战在上一节里,我们学习了微服务的来源和主要特点。对于软件测试人员而言,微服务 架构对软件测试带来了哪些新的挑战呢?我们应该用什么样的策略和

12、方法来迎接这些挑 战?总体的测试策略软件测试的目的是确保软件产品的质量符合预期。衡量测试质量的指标有很多,最常 见的是测试覆盖率和测试成本(包括测试所用时间、测试维护成本),而衡量测试效果的 主要手段则是最终产品在实际使用中暴露出来的问题数量(Bug Number)。具体到采用微服务架构的产品而言,Martin Fowler在关于软件测试的论述中提出了其目的:开发团队采用的任何测试策略,都应当力求为服务内部每个模块的完整性,以及每个 模块之间、各个服务之间的交互,提供全面的测试覆盖率,同时还要保持测试的轻便快 捷。因此,我们需要采取下面几点测试策略:我们一方面要保证从各个维度上,无一遗漏地对微

13、服务进行全面的测试,特别是对于 分布式的系统,系统的所有层次都必须被覆盖到;另一方面又要确保测试执行的快捷,这 样才能保证持续集成/持续交付(CI/CD)的实现。要确保测试策略的正确实施,工具和技术固然重要,然而,首先需要测试人员在团队 中树立起提倡质量第一的测试文化:无法通过测试的代码不应该被合并到代码仓库里;无法通过测试的代码不应该被发布出去。不能为了测试而测试,测试的真正目的是为了交付高质量的软件给用户,而不是把资 源浪费在没有实际意义的测试用例上。所有的测试层次、流程和用例,都应该有的放矢。传统测试方法面临的挑战以一个常见的开发团队为例,在采用了微服务架构之后,很可能同时会开发多个模块

14、 (即微服务),每个微服务有不同的客户要求、开发周期、开发进度和交付期限,但是整 个团队又必须保证能够在固定的时间节点(譬如每月一次、每两周一次,甚至每天一次或 者多次),持续地、稳定地为用户提供可以部署、使用的产品。这意味着,过去那种先等 产品经理、业务部门提供需求,开发人员再进行开发,最后交给测试人员执行集成测试、 端到端测试的方法,已经无法提供足够的测试粒度和足够快的响应速度。归结起来,与基于单体式架构的传统测试方法相比,微服务架构对测试提出了以下挑 战:服务/模块/层次(layer)之间存在复杂的依赖性。在单体式架构中,通常使用集成测试来验证依赖是否正常。而在微服务架构中,服务 数量往

15、往很多,每个服务都是独立的业务单元,服务之间主要通过接口进行交互,如何保 证这些依赖的正常,是测试人员面临的主要挑战。这意味着,如果想单独测试某一个服 务,或者服务中的某个模块,就必须剥离它们对于其他环节的依赖关系。这需要通过 Mock、Stub等方法来实现。不同的服务可能会在不同的环境/设置下运行。特别是一些后端服务,与前端服务的运行环境可能截然不同。这时在考虑对每种服务 设立自动化管线时,就必须有针对性的设置相应的环境配置。而且,在微服务架构中,每 个服务都独立部署,交付周期短且频率高,人工部署已经无法适应业务的快速变化。因此 如何有效地构建自动化部署体系,保证配置的稳定性、可重复性,是微

16、服务测试面临的另 一个挑战,必须与DevOps人员一同解决。涉及多个服务的UI端到端测试(End-to-End测试,简称E2E测试)非常容易出 错。因为每种服务的开发进度不同,集成不同服务的端到端测试往往会因为某一个服务的 微小改动而出错。这种出错是测试人员希望避免的干扰信息。这意味着,对端到端测试的 设计,必须采取一定的防干扰、防误报策略。测试结果可能取决于网络的稳定性。微服务架构是基于分布式的系统,而构建分布式系统必然会带来额外的开销。性能:分布式系统是跨进程、跨网络的调用,受网络延迟和带宽的影响。可靠性:由于高度依赖于网络状况,任何一次的远程调用都有可能失败,随着服务的 增多还会出现更多

17、的潜在故障点。因此,如何提高系统的可靠性、降低因网络引起的故障 率,是系统构建的一大挑战。异步:异步通信大大增加了功能实现的复杂度,并且伴随着定位难、调试难等问题。数据一致性:要保证分布式系统的数据强一致性,成本是非常高的,需要在C(- 致性)A (可用性)P (分区容错性)三者之间做出权衡。特别是涉及到数据存储和外部通信的部分,如果在测试中不摆脱这些因素的影响,就 可能会得到一些随机性的误报,干扰测试结果。故障分析的复杂度会随着服务的增加而提高。微服务架构中,因为每个服务都需要独立地配置、部署、监控和收集日志,因此在发 现问题之后,进行诊断分析时,搜集缺陷信息的成本呈指数级增长。与交付周期不

18、同的开发团队之间的交流成本。这一点虽然跟技术无关,但是实际上会对测试人员的工作造成很大的困扰。因为开发 模式分解为负责不同服务的多个小组,测试人员往往每天要花费大量的时间,了解不同团 队的开发进度。如果还需要手动进行回归测试(Regression Test),最终将会不堪重负。 所以自动化测试是必须采取的手段和方向。如何应对这些挑战,我总结了下面这三个原则:自动化:测试任务的增加,要求测试人员必须把主要的精力用于将测试自动化,摆 脱手动测试带来的沉重负担。当然,自动化测试必须足够稳定、稳健,不能动辄误报,否 则反而会导致很高的维护成本。层次化:这意味着采用分层次的测试方法,粒度由细到粗,范围由

19、小到大。这就是Mike Cohn提出的测试金字塔(Test Pyramid ),其中最重要的两个原则 是:应该用不同的粒度来测试应用程序;层次越高,测试越少。最底层的是单元测试(Unit Test),粒度最细,速度最快,维护成本也最低。往上是 针对每种服务内部的各种模块、业务流程的测试。最上面是基于前端UI的测试,这部分 的粒度最粗,范围最大(因为会覆盖大多数服务),但是维护成本最高,因为稍微有些细 微的变化就可能需要调整脚本。而且,由于基于前端,需要设置很多响应时间和等待时 间,所以速度最慢。Mike Cohn是Scrum软件开发方法的提出者之一,也是Scrum联盟的创始成 员。他目前是Mo

20、untain Goat Software公司的所有者,致力于提供关于Scrum和 Agile软件开发技术的培训。可视化:为了降低交流成本,最好的办法就是让所有的测试结果可视化。这意味着 将构建(Build)、测试(Test)、部署(Deploy)所有这些相关任务构建在一个流水线 之中,让所有团队成员都可以随时监控项目进度,找到阻碍项目的瓶颈。以下面这个典型团队为例,整个从开发、测试、构建到部署的一系列过程,都可以借 助Jenkins或者TeamCity这样的任务调度工具,完全可视化,再借助SonarQube这 样的代码质量监控工具监控测试结果。Google Analytics或者Microso

21、ft的Azure ApplicationInsight等云端监控工具,则可以提供实时生产环境的客户使用信息或者测试 数据,让整个团队可以随时把握产品的整个流水线的运行状态。在微服务架构中所采用的主要测试方法。如下图所示,它们主要包括:End to EndTeslsDeploy In QA EnvironmentIntegration Tests5Code DevelopmentUnit TestsBuild Service单元测试(Unit Test)用于验证微服务内部的类方法或函数的行为。它们会根据测试框架,执行代码文件里 的类方法或函数,提供不同的输入,并验证与每一个输入相对应的输出。集成

22、测试(Integration Test)用于验证微服务与外部模块的通信或者交互行为。测试框架会启动服务的一个实例, 并调用服务的外部接口来执行业务逻辑。组件测试(Component Test)即验证微服务能否起到预期的作用。这需要把微服务周边依赖的所有其他服务或者资 源全部模拟化,从该服务外部用户的角度来检查服务能否提供预期的输出。端到端测试(End-to-end Test)验证整个系统的功能能否符合用户的预期,一般是从UI层面进行测试,确保用户体 验完全达到客户要求。探索测试(Exploratory Test,即手动测试)这一步通常由业务专家型用户执行,具体查看某个新添加的特性是否开发、部署

23、成 功。总结简单总结一下所学习的内容:微服务架构对软件测试提出了很多全新的挑战。应对这些挑战的方法包括:自动化层次化可视化怎么针对微服务架构做单元测试?单元测试是开发人员编写的一小段代码,用于检验被测代码的一个很小的、很明确的 功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特 定函数的行为。例如,你可能把一个很大的值放入一个有序list中去,然后确认该值出现 在list的尾部。或者,你可能会从字符串中删除匹配某种模式的字符,然后确认字符串确 实不再包含这些字符了。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C 语言中单元指一个函数,Jav

24、a里单元指一个类,前端应用中可以指一个窗口或一个菜单 等。总的来说,单元就是人为规定的最小的被测功能模块。我们将探讨在微服务架构下,单元测试的设计、实现和质量控制。设计:定义测试边界要设计高效率(既运行快速又覆盖率高)的单元测试,首要要准确地定义测试边界。 测试的目的就是为了验证边界里黑盒的行为是否符合预期,我们向黑盒输入数据,然 后验证输出的正确性。在单元测试里,黑盒指的是函数或者类的方法,目的是单独测试特 定代码块的行为。但是在微服务架构中,很多时候黑盒的输出需要依赖于其他的功能或者服务,即存在夕卜部依赖。为了更好地理解这个概念,我们以一个简单的注册功能为例:输入参数;UserEiame-

25、 WangdongPassword 明乂密暗从图中可以看出,这个函数包含了一些输入和输出。输入参数包括基本的用户注册信息(姓名、用户名和密码),而返回新创建的用户ID。但是在此过程中,还有一些不是很明显的输入数据。这个函数调用了两个外部函数: db.user.inser()是向数据库插入数据;Password.hashAndsave()是一个微服务,用于生 成密码的哈希值,再加以保存。在某些情况下,数据库可能会返回错误,比如用户名已经 存在,导致数据库插入失败。另外,因为需要调用外部的微服务生成密码哈希值,如果网 络连接出现问题,或者哈希值生成服务由于发生过载而导致服务超时,那么密码保存就会

26、返回错误。User.create()函数必须能够妥善地处理这两种错误,这是测试的重点。也就是说,为了全面地测试用户注册功能,单元测试所要做的不仅仅是简单地输入各 种不同的参数,它还要能够让外部函数/微服务,能够产生出指定的错误,再验证函数的错 误处理逻辑是否符合预期。因此,为了在不依赖于外部条件的情况下制造出各种输入数据,就需要使用Stub或 者Mock,中文可以理解为对函数外部依赖的模拟器。简而言之,它意味着用一个假的版 本替换了真实的对象(例如一个类、模块、函数或者微服务)。假的版本的行为特征和真实对象非常类似,采用相同的调用方法,并按照你在测试开始之前预定义的返回方式,提 供返回数据。测

27、试框架在运行被测试的函数时,可以把对外部依赖函数服务的调用,重定 向到Stub 上,这样单元测试就可以在没有外部服务的情况下进行,即保证了速度,又避 免了网络条件的影响。这里再强调下Stub和Mock的区别,很多人经常搞混。Stub就是一个纯粹的模 拟器,用于替代真实的服务/函数,收到请求返回指定结果,不会记录任何信息。Mock 则更进一步,还会记录调用行为,可以根据行为来验证系统的正确性。创建Stub的工具有很多,包括Node.js/JavaScript框架下的sinon.js, testdouble.js 等;Python 下的 mock 等。在刚刚提到的注册函数和密码哈希值生成、保存服务

28、之间我们可以使用模拟器来达到各种目的:模拟器可返回任意的设定值,用于模拟外部函数的输出。这在测试罕见的边界情况时 会非常有用,比如有些错误场景可能很少发生或者非常难以重现。模拟器也可以捕捉被测试函数传给外部函数的参数,或者把这些参数记录下来。这样 就可以验证被测试函数需要调用哪些外部函数,以及需要传给外部函数哪些参数。通过对外部依赖函数使用模拟器,通常可以在几秒钟内,执行数千个单元测试。这 样,开发人员就可以把单元测试加入到日常的开发工作管线(Pipeline)当中,包括直接 集成到常用的IDE里,或者通过终端命令行触发。通过在编写代码的同时,频繁运行单 元测试,有助于尽早发现代码中的问题。对

29、于程序员来说,如果养成了对自己写的代码进 行单元测试的习惯,不但可以写出高质量的代码,而且还能提高编程水平。顺便说一句,在微服务架构中,单元测试的作用不仅限于代码开发,它们还对 DevOps/CI (持续集成)有很大的帮助,可以集成到代码合并(Merge)流程里。譬如,GitHub支持对一些主流CI服务的状态检查。一般它会限制对Master”主 分支的提交权限,不允许开发人员直接向该分支提交代码,而是要求他们把代码先提交到 其他分支上(提交Pull Request),再由其他开发人员进行代码审查(CodeReview )。最后,在将代码合并到主分支的时候,GitHub要求先通过状态检查。这时,

30、 Jenkins、CircleCI和TravisCI等CI服务都提供了状态检查钩子(hook),它们会从 分支上获取代码并运行单元测试。如果通过了,就允许合并代码,否则就不允许实现:单元测试的流程单元测试的工具有很多,例如:C+ : Googletest、GMockJava : Junit、TestNG、Mockito、PowerMockJavaScript: Qunit、JasminePython : unittestLua : luaunit一个单元测试的实现主要分为以下几步:设置测试数据;在测试中调用你的方法;判断返回的结果是否符合预期。这三步可以简化为三A原则” :Arrange (设置)、Act (调用)、Assert (检 查)。或者也可以借用BDD (行为驱动测试)的概念,把单元测试的流程分为三步:Given (上下文)、When (事件)、Then (结果)。卜面我们来看一个真实的例子,这是一个名为Examplecontroller的类,用于在人 名库(PersonRepository)中查找人名。下面,我们将用Junit,对类中的hello( lastn

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论