​无需测试环境!如何利用测试脚手架隔离微服务,实现功能自动化

news2024/12/22 14:23:42

想在不建立完整测试环境的情况下测试微服务?

想在将变更推送到主线分支之前完成测试?

这是我们在进行项目交付时经常遇到的难题。最近,当我们开始一个新的项目,为客户构建一个新的聚合平台时,我们希望将尽可能多的测试和自动化转移到流程的合并前执行。

我们知道,我们需要一种方法来对我们的服务执行深度功能测试,并使我们的团队能够独立运行。这就是促使我们开发用于微服务隔离和测试的测试脚手架(Test Scaffolding)的原因。

挑战

在部署到集成环境之前,我们需要独立于其他服务对微服务的变更进行测试。这样,当我们将变更部署到上层环境时,就可以专注于测试集成和事务流,而不是单个服务的功能行为。

虽然我们可以将测试中的服务指向其依赖的测试环境,但这可能会导致两个问题。

  • 首先,环境用户之间的数据混合会导致意想不到的行为。

  • 其次,由于服务之间的连锁调用,可能会出现延迟问题和测试缓慢。我们很快意识到,我们需要在合并前隔离服务的能力,以实现集中和完全隔离的功能测试自动化。

方法

我们决定通过引入一个名为测试脚手架Test Scaffolding的概念来隔离服务。

测试脚手架的工作是模拟微服务可能需要与之交互的所有外部服务。测试脚手架的作用与建筑脚手架的作用类似,随着微服务交互的增加,会将其添加到脚手架中。每个服务都有自己的脚手架,与服务预合并(the service pre-merge)一起构建和部署。

这样就为我们的微服务提供了它所期望的依赖关系,并通过测试对输入和输出进行细粒度控制。

这种方法对于独立构建服务并向我们的工程团队提供持续的早期反馈也至关重要。

微服务基础知识


我们将在下面的示例中使用 AWS 术语,但这些概念应适用于所有平台。

开始之前,我们必须对如何定义与测试脚手架交互的功能测试范围有一个共同的理解:

  • 测试仅限于特定微服务的边界。

  • 测试已部署服务的交互属于我们的行程测试(journey tests)。

  • 在项目中,我们将微服务的边界定义为服务CloudFormation Template(CFT)所定义的组件。

微服务中最常见的组件通常是API Gateway、Lambdas、Dynamo 和 SNS 或 SQS 的组合,我们可以用这样的图来表示:

图片

在这个例子中,假设我们需要测试微服务A,微服务A在给定的数据流中与微服务B和C通信。由于这些服务的目的非常集中,因此它们必须联系其他服务来验证外部信息。

图片

微服务A与微服务B 、C对话

我们不想让B和C的实例来测试A,所以我们需要隔离一些交互:

图片

挑战:将A与B和C隔离


这正是测试脚手架的作用。它取代了服务B和C的位置,在测试中提供了大量关于如何设置和管理输入和输出的控制。

 

图片


 

有几点需要注意:

  • 每个服务只有一个测试脚手架。我们可以根据需要在单个测试脚手架中添加多种不同类型的交互--即使它们代表不同服务的不同端点。

  • 微服务和测试脚手架都由各自的 CloudFormation 模板(CFT)定义,但存储在同一个项目 repo 中。

使用案例示例:

既然我们已经定义了微服务的边界,那就来讨论几个用例吧。

在第一个示例中,服务接受 API 请求,处理请求,然后通过 SNS 主题发送输出。我们在项目中使用这种模式将信息发送到不同的聚合器服务,这些聚合器服务会将数据映射成正确的格式,然后发布到多个第三方服务。

我们要确保输入值得到正确处理,并输出到 SNS 主题。与 SNS 交互非常棘手,因为它需要订阅才能捕获响应。这很难在自动测试中即时完成,因此我们添加了一个 Lambda 来处理对 SNS 主题的订阅,并添加了 DynamoDB 来保留响应,以便对测试脚手架进行验证。
 

图片

这样,测试就可以向服务发送请求,让服务处理请求,然后从脚手架 DynamoDB 中提取发送到 SNS 主题的结果,以确定结果是否符合我们的预期。

第二个示例与第一个示例类似。在这里,我们的微服务从 SNS 主题获取事件,并调用服务进行额外的状态验证,例如产品是否处于活动状态。然后,该状态将发布到映射的外部服务。

在这种情况下,我们的测试脚手架将包含 SNS 主题、API 网关、Lambda 和 Dynamo。测试会将我们希望服务从测试脚手架 API 调用中接收的数据填充到 Dynamo 中,并触发 SNS 主题流以启动测试。在处理过程中,微服务会将其状态调用发送到测试脚手架中的 API 网关,并获取测试在此暂存的响应。我们在微服务的 DynamoDB 中验证流程的最终输出。

图片

如示例所示,使用测试脚手架可以让我们高度灵活地匹配服务的交互要求,从而可以完全隔离地测试每个服务。

脚手架设置、部署和执行

那么,我们是如何配置这些服务从而确保它们在正确的区域内运行的呢?

为了取得成功,我们在云组建模板中进行了参数化。测试脚手架和被测微服务都有各自的 CFT 来管理必要的资源。在微服务 CFT 中,我们利用模板参数化和条件语句来控制测试脚手架的使用,具体取决于服务的部署方式。这些参数通过 CI/CD 管道传入。在我们的预合并部署中,这可以让微服务寻找测试脚手架,而在集成环境中,我们可以让该服务寻找适当的已部署服务。

下面是根据输入参数有条件使用测试脚手架的微服务 CFT 片段:


Conditions:    
condIsProd: !Equals [ !Ref paramEnvironment, prod ]    
condNotFeatureBranch: !Equals [!Ref paramFeatureBranch, ""]    
condIsLocal: !Equals [ !Ref paramEnvironment, local ]    
condIsLocalOrBranch: !Or [ !Not [ Condition: condNotFeatureBranch ], Condition: condIsLocal ]    




Resources:    


resLambdaConvertPicture:    
Type: AWS::Serverless::Function    
Properties:    
Handler: ConvertPicture.handler    
FunctionName: !Sub "${paramEnvironment}${paramFeatureBranch}_${paramServiceName}_ConvertPicture"    
CodeUri: dist/ConvertPicture.js    
Policies:    
- SNSPublishMessagePolicy:    
TopicName: !If    
- condIsLocalOrBranch    
- !Sub "${paramEnvironment}${paramFeatureBranch}_${paramServiceName}-ts_catalog_pictureUploaded"    
- !Sub "${paramEnvironment}${paramFeatureBranch}_catalog_PictureUploaded"    
- DynamoDBCrudPolicy:    
TableName: !Ref resDynamoMenuJobsTable

测试脚手架作为分支部署的一部分与微服务同时部署。下图显示了脚手架代码必须遵守与其他服务相同的部署标准。部署完成后,我们将运行功能测试。

图片

带有测试脚手架的分支部署CI/CD管道图。

只有当使用脚手架的隔离服务测试通过后,才有可能并入主线分支。测试脚手架不会部署到我们的集成环境中。一旦主线中出现变更,我们就会使用计划作业来清理任何可能尚未手动拆除的隔离环境(服务和脚手架)。这一点很重要,因为给定账户的可用资源数量是有上限的。

测试,更上一层楼

测试脚手架允许我们的团队在合并到主线分支并部署到集成环境之前,使用与服务开发完全相同的技术(CFT 和本地 AWS 资源)独立开发和测试微服务功能。通过脚手架控制输入和输出,我们可以围绕关键流程建立更深入的测试集,并有效地确保高质量。

由于测试脚手架允许我们创建的不仅仅是基本的创建、读取更新和删除测试,我们还能构建与业务用例相关联的深度功能流。这些深入的功能测试让我们对微服务在合并到主线并部署到环境中之前的变化充满信心。此外,将大部分自动化保持在这一级别(以及单元测试)可最大限度地减少行程测试(journey test)(即:"端到端测试")的使用,并支持测试金字塔所有级别的健康自动化套件。

一些亮点:

  • 每次推送到版本库时都会执行测试。

  • 测试速度快!针对我们的一项主要服务进行的几百个测试中,运行时间最长的一组测试不到10分钟。

  • 测试对即将进行的合并进行把关。如果测试不通过,你的提交就无法合并,也不会影响其他团队在其他服务上的工作。

  • 测试定义并控制自己的所有数据,使其具有完全的确定性和密封性。

经验教训

在我们学习实施和利用测试脚手架的最佳方法时,需要不断地尝试和犯错。我们既要确定如何将其纳入项目,又要确定将其作为管道的一部分进行构建和部署的最佳方式。以下是一些关键经验:

KISS-("Keep It Simple, Stupid")我们倾向于将测试脚手架中的行为复杂化,从而不需要重复依赖逻辑来生成适当的响应。

清理- 当变更提交到主线时,服务和测试脚手架的测试版本就会被遗留下来。因此,我们建立了清理工作,以便从AWS开发账户中删除不再处于活动开发阶段或已数天未更新的工件。事实上,一切都由云形成堆栈定义,这使得根据需要重新部署变更变得非常容易。

契约演进(Contract evolution) - 测试脚手架中使用的请求和响应与实际服务契约保持一致至关重要。否则,就无法测试应该测试的内容。团队间的沟通和合同测试仍然至关重要。

与任何新流程一样,测试脚手架的开发和部署也需要时间和精力,但它最终成为了我们整体微服务开发方法中的一个强大工具和重要资产。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/950458.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java线程 - 详解(2)

一,线程安全问题 有些代码在单个线程的环境下运行,完全正确,但是同样的代码,让多个线程去执行,此时就可能出现BUG,这就是所谓的 "线程安全问题"。举一个例子: public class Demo {s…

Ansible学习笔记12

playbook: playbook(剧本):是ansible用于配置、部署和管理被控节点的剧本,用于Ansible操作的编排。 使用的是yaml格式,(saltstack、elk、docker、docker-compose、k8s都会使用到yaml格式。&am…

c语言实现二叉树(链式结构)

文章目录 前言一、二叉树的遍历1、二叉树的层序遍历2、二叉树的前序遍历3、二叉树的中序遍历4、二叉树的后序遍历5、代码实现 二、二叉树的一些操作的实现1、求二叉树的结点个数2、求二叉树叶子结点个数3、求二叉树第k层结点个数4、求二叉树深度5、二叉树中查找值为x的结点6、二…

彻底理解浏览器的缓存机制

前言 在前端性能优化的方式中,最重要的当然是缓存了,使用好了缓存,对项目有很大的帮助。比如我们访问网页时,使用网页后退功能,会发现加载的非常快,体验感很好,这就是缓存的力量。 什么是缓存…

SpringBoot的四种handler类型

Controller ReuestMapping 实现Controller接口 使用Component将该类封装成一个Bean 实现HttpRequestHandler 实现RouterFunction

upload-labs文件上传漏洞靶场练习

任意文件上传靶场upload-labs下载地址 文章目录 Pass-01- 前端JS校验绕过Pass-02- 文件类型MIME类型绕过Pass-03- 文件名后缀黑名单绕过Pass-04- .htaccess绕过Pass-05- 文件名后缀大写绕过Pass-06- 文件名后缀加空格绕过Pass-07- 文件名后缀加点绕过Pass-08-文件名后缀 ::$DAT…

控制goroutine 的并发执行数量

goroutine的数量上限是1048575吗? 正常项目,协程数量超过十万就需要引起重视。如果有上百万goroutine,一般是有问题的。 但并不是说协程数量的上限是100多w 1048575的来自类似如下的demo代码: package mainimport ( "fmt" "ma…

SpringMVC使用

文章目录 一.MVC基础概念1.MVC定义2.SpringMVC和MVC的关系 二.SpringMVC的使用1.RequestMapping2.获取参数1.获取单个参数2.传递对象3.后端参数重命名(后端参数映射)4.获取URL中参数PathVariable5.上传文件RequestPart6.获取Cookie/Session/header 3.返回…

电视盒子什么牌子好?经销商整理线下热销电视盒子品牌排行榜

在面对众多品牌和型号时,不知道电视盒子哪个牌子好的消费者超多,很多人进店都会问我电视盒子哪款好?我根据店铺内近两年的销量情况整理了电视盒子品牌排行榜,看看实体店哪些电视盒子最值得入手吧。 TOP 1.泰捷WEBOX 40Pro Max电视…

案例实操-获取员工数据

案例:获取员工数据,返回统一响应结果,在页面渲染展示 package com.bignyi.controller;import com.bignyi.pojo.Emp; import com.bignyi.pojo.Result; import com.bignyi.utils.XmlParserUtils; import org.springframework.web.bind.annotat…

分享一个在线二维码生成器(基于qrcode.js开发)

一种二维码扫描与生成的工具, 它可生成个性化二维码, 支持文本、网址、图片、短信、电话等格式及主题,提供融合码功能 演示地址 https://qrcode.gitapp.cn 关键代码 var qrcode new QRCode(document.getElementById("qrcode"), {text: "",width: 288,h…

2023下半年西安/北京/深圳NPDP产品经理国际认证开班啦

产品经理国际资格认证NPDP是新产品开发方面的认证,集理论、方法与实践为一体的全方位的知识体系,为公司组织层级进行规划、决策、执行提供良好的方法体系支撑。 【认证机构】 产品开发与管理协会(PDMA)成立于1979年,是…

【JavaSE】Java快速入门

Java main 函数 public class Main {public static void main(String[] args) { System.out.printf("Hello and welcome!");} }与C命名规范不同,Java 的命名形式最好使用驼峰法 Java 注释 C/C常用的两种注释习惯Java都可以使用,Java自身…

【HASH值获取】

命令行输入:C:\Users\Administrator>certutil -hashfile SIC-1000.exe md5

2、[春秋云镜]CVE-2022-30887

文章目录 一、靶标介绍二、复现过程 一、靶标介绍 二、复现过程 (1)打开网址。 (2)查看源代码 邮件格式:第一个符号不准为,后续符号有、.; 密码格式:匹配所有小写字母&#xff0c…

数组 刷题常用

在写数组模拟常用到数组,借此把常用的记下来以便查阅 一维数组,若初始化为0,可以用int a[N] {0}或者int a[N]{}. 但是若是其他值,不可类似地初始化为int a[N] {0},而应写成memset或者fill赋值的方法。 首先便是二维…

康希诺的再估值:市场到底,行业向上

生物医药是整个二级市场弹性数一数二,但拐点难以揣摩的行业。这一点,美港A三大市场都曾经有过足够多的暴涨暴跌案例可用于佐证。 但很多时候,这种片面的表现又掩盖了生物医药自身的永续价值:在绝大多数细分赛道上,任何…

激活Conda环境并在pycharm使用

第一步:打开Anaconda Prompt 第二步:查看当前存在的虚拟环境 conda env list 第三步:创建虚拟环境 conda create -n 环境名 pythonX.X.X 如果不清楚python版本,可以用以下命令查看: 第四步:激活指定虚拟环…

客户案例|MemFire Cloud助推应急管理业务,打造百万级数据可视化大屏

「导语」 硬石科技,成立于2018年,总部位于武汉,是一家专注于应急管理行业和物联感知预警算法模型的技术核心的物联网产品和解决方案提供商。硬石科技作为一家高新技术企业,持有6项发明专利,拥有100余项各类平台认证和资…