第02课:微服务对软件测试提出的挑战
在上⼀课⾥,我们学习了微服务的来源和主要特点。对于软件测试⼈员⽽⾔,微服务架构对软件测试带来了哪些新的挑战呢?我们应该⽤什么样的策略和⽅法来迎接这些挑战?
总体的测试策略
软件测试的⽬的是确保软件产品的质量符合预期。衡量测试质量的指标有很多,最常见的是测试覆盖率和测试成本(包括测试所⽤时间、测试维护成本),⽽衡量测试效果的主要⼿段则是最终产品在实际使⽤中暴露出来的问题数量(Bug Number)。
具体到采⽤微服务架构的产品⽽⾔,Martin Fowler 在关于软件测试的论述中提出了其⽬的:
开发团队采⽤的任何测试策略,都应当⼒求为服务内部每个模块的完整性,以及每个模块之间、各个服务之间的交互,提供全⾯的测试覆盖率,同时还要保持测试的轻便快捷。
因此,我们需要采取下⾯⼏点测试策略:
我们⼀⽅⾯要保证从各个维度上,⽆⼀遗漏地对微服务进⾏全⾯的测试,特别是对于分布式的系统,系统的所有层次都必须被覆盖到;
另⼀⽅⾯⼜要确保测试执⾏的快捷,这样才能保证持续集成/持续交付(CI/CD)的实现。
要确保测试策略的正确实施,⼯具和技术固然重要,然⽽,⾸先需要测试⼈员在团队中树⽴起提倡质量第⼀的“测试⽂化”:⽆法通过测试的代码不应该被合并到代码仓库⾥;
⽆法通过测试的代码不应该被发布出去。
不能为了测试⽽测试,测试的真正⽬的是为了交付⾼质量的软件给⽤户,⽽不是把资源浪费在没有实际意义的测试⽤例上。所有的测试层次、流程和⽤例,都应该有的放⽮。
传统测试⽅法⾯临的挑战
以⼀个常见的开发团队为例,在采⽤了微服务架构之后,很可能同时会开发多个模块(即微服务),每个微服务有不同的客户要求、开发周期、开发进度和交付期限,但是整个团队⼜必须保证能够在固定的时间节点(譬如每⽉⼀次、每两周⼀次,甚⾄每天⼀次或者多次),持续地、稳定地为⽤户提供可以部署、使⽤的产品。这意味着,过去那种先等产品经理、业务部门提供需求,开发⼈员再进⾏开发,最后交给测试⼈员执⾏集成测试、端到端测试的⽅法,已经⽆法提供⾜够的测试粒度和⾜够快的响应速度。
归结起来,与基于单体式架构的传统测试⽅法相⽐,微服务架构对测试提出了以下挑战:
服务/模块/层次(layer)之间存在复杂的依赖性。
在单体式架构中,通常使⽤集成测试来验证依赖是否正常。⽽在微服务架构中,服务数量往往很多,每个服务都是独⽴的业务单元,服务之间主要通过接⼝进⾏交互,如何保证这些依赖的正常,是测试⼈员⾯临的主要挑战。这意味着,如果想单独测试某⼀个服务,或者服务中的某个模块,就必须剥离它们对于其他环节的依赖关系。这需要通过 Mock、Stub 等⽅法来实现。
不同的服务可能会在不同的环境/设置下运⾏。
特别是⼀些后端服务,与前端服务的运⾏环境可能截然不同。这时在考虑对每种服务设⽴⾃动化管线时,就必须有针对性的设置相应的环境配置。⽽且,在微服务架构中,每个服务都独⽴部署,交付周期短且频率⾼,⼈⼯部署已经⽆法适应业务的快速变化。因此如何有效地构建⾃动化部署体系,保证配置的稳定性、可重复性,是微服务测试⾯临的另⼀个挑战,必须与 DevOps ⼈员⼀同解决。
涉及多个服务的 UI 端到端测试(End-to-End 测试,简称 E2E 测试)⾮常容易出错。
因为每种服务的开发进度不同,集成不同服务的端到端测试往往会因为某⼀个服务的微⼩改动⽽出错。这种出错是测试⼈员希望避免的⼲扰信息。这意味着,对端到端测试的设计,必须采取⼀定的防⼲扰、防误报策略。
测试结果可能取决于⽹络的稳定性。
微服务架构是基于分布式的系统,⽽构建分布式系统必然会带来额外的开销。
性能: 分布式系统是跨进程、跨⽹络的调⽤,受⽹络延迟和带宽的影响。
可靠性: 由于⾼度依赖于⽹络状况,任何⼀次的远程调⽤都有可能失败,随着服务的增多还会出现更多的潜在故障点。因
此,如何提⾼系统的可靠性、降低因⽹络引起的故障率,是系统构建的⼀⼤挑战。
异步: 异步通信⼤⼤增加了功能实现的复杂度,并且伴随着定位难、调试难等问题。
数据⼀致性: 要保证分布式系统的数据强⼀致性,成本是⾮常⾼的,需要在 C(⼀致性)A(可⽤性)P(分区容错性)三者
之间做出权衡。
特别是涉及到数据存储和外部通信的部分,如果在测试中不摆脱这些因素的影响,就可能会得到⼀些随机性的误报,⼲扰测试
结果。
故障分析的复杂度会随着服务的增加⽽提⾼。
微服务架构中,因为每个服务都需要独⽴地配置、部署、监控和收集⽇志,因此在发现问题之后,进⾏诊断分析时,搜集缺陷信息的成本呈指数级增长。
与交付周期不同的开发团队之间的交流成本。
这⼀点虽然跟技术⽆关,但是实际上会对测试⼈员的⼯作造成很⼤的困扰。因为开发模式分解为负责不同服务的多个⼩组,测试⼈员往往每天要花费⼤量的时间,了解不同团队的开发进度。如果还需要⼿动进⾏回归测试(Regression Test),最终将会不堪重负。所以⾃动化测试是必须采取的⼿段和⽅向。
如何应对这些挑战,我总结了下⾯这三个原则:
1.⾃动化:测试任务的增加,要求测试⼈员必须把主要的精⼒⽤于将测试⾃动化,摆脱⼿动测试带来的沉重负担。当然,⾃动化测试必须⾜够稳定、稳健,不能动辄误报,否则反⽽会导致很⾼的维护成本。
2.层次化:这意味着采⽤分层次的测试⽅法,粒度由细到粗,范围由⼩到⼤。下图说明了⼏个主要层次之间的关系:
这就是 Mike Cohn 提出的测试⾦字塔(Test Pyramid),其中最重要的两个原则是:
应该⽤不同的粒度来测试应⽤程序;
层次越⾼,测试越少。
最底层的是单元测试(Unit Test),粒度最细,速度最快,维护成本也最低。往上是针对每种服务内部的各种模块、业务流程的测试。最上⾯是基于前端 UI 的测试,这部分的粒度最粗,范围最⼤(因为会覆盖⼤多数服务),但是维护成本最⾼,因为稍微有些细微的变化就可能需要调整脚本。⽽且,由于基于前端,需要设置很多响应时间和等待时间,所以速度最慢。
Mike Cohn 是 Scrum 软件开发⽅法的提出者之⼀,也是 Scrum 联盟的创始成员。他⽬前是 Mountain Goat Software 公司的所有者,致⼒于提供关于 Scrum 和 Agile 软件开发技术的培训。
3.可视化:为了降低交流成本,最好的办法就是让所有的测试结果可视化。这意味着将构建(Build)、测试(Test)、部署(Deploy)所有这些相关任务构建在⼀个流⽔线之中,让所有团队成员都可以随时监控项⽬进度,到阻碍项⽬的瓶颈。
以下⾯这个典型团队为例,整个从开发、测试、构建到部署的⼀系列过程,都可以借助 Jenkins 或者 TeamCity 这样的任务调度⼯具,完全可视化,再借助 SonarQube 这样的代码质量监控⼯具监控测试结果。Google Analytics 或者 Microsoft 的 Azure ApplicationInsight 等云端监控⼯具,则可以提供实时⽣产环境的客户使⽤信息或者测试数据,让整个团队可以随时把握产品的整个流⽔线的运⾏状态。
本达⼈课的后⾯⼏节内容,将会以层次化的⽅式,逐⼀介绍在微服务架构中所采⽤的主要测试⽅法。如下图所⽰,它们主要包括:单元测试(Unit Test)
⽤于验证微服务内部的类⽅法或函数的⾏为。它们会根据测试框架,执⾏代码⽂件⾥的类⽅法或函数,提供不同的输⼊,并验证与每⼀个输⼊相对应的输出。
集成测试(Integration Test)
⽤于验证微服务与外部模块的通信或者交互⾏为。测试框架会启动服务的⼀个实例,并调⽤服务的外部接⼝来执⾏业务逻辑。
组件测试 (Component Test)
即验证微服务能否起到预期的作⽤。这需要把微服务周边依赖的所有其他服务或者资源全部模拟化,从该服务外部“⽤户”的⾓度来检查服务能否提供预期的输出。
端到端测试(End-to-end Test)
验证整个系统的功能能否符合⽤户的预期,⼀般是从 UI 层⾯进⾏测试,确保⽤户体验完全达到客户要求。
探索测试( Exploratory Test,即⼿动测试)
这⼀步通常由业务专家型⽤户执⾏,具体查看某个新添加的特性是否开发、部署成功。
本课总结
分布式和微服务的关系简单总结⼀下本课程所学习的内容:
1. 微服务架构对软件测试提出了很多全新的挑战。
2. 应对这些挑战的⽅法包括:
⾃动化
层次化
可视化

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。