前端中台化,把格局做大——NodeJS 和测试服务摸索
近些年,「NodeJS 应该如何在公司业务中实在落地」这类问题不足为奇。自从 2009 年 NodeJS 诞生之后,抢尽风头,圈粉有数。但肯定有工程师不禁要质疑「NodeJS 真的曾经开拓天地,占据架构体系的一席之地了吗」,「国外据说 NodeJS 发展热火朝天,国内当初到底是个什么状态」,「听到过阿里 NodeJS 扛下双十一,到底是什么状况」
没错,风头越大,质疑也就越多。当争议和浮华褪去,技术的落地:就让上帝的归上帝,撒旦的归撒旦。本系列文章,我将会梳理 NodeJS 在我司以及在国内外其余团队的典型我的项目案例,并深入探讨 NodeJS 的发展前景和最佳实际。感兴趣的读者能够订阅 前端杂谈 或 LucasHC,心愿对大家有所启发。
开篇小茶歇
我的一个相干答复:前端应该如何在公司业务中落地 NodeJS?
另外,本篇文章内容较长,波及到了:端到端测试,NodeJS 服务,中台能力(docker 镜像等基础设施),基于图片比对的插件实现等。倡议赞后珍藏~ 也能够先间接跳到结尾彩蛋环节~
命中注定的缘分 —— 当 NodeJS 遇见端到端测试困局
端到端测试,也叫做 UI 测试,e2e 测试。用文言说,相似常见的自动化测试,它站在用户应用的角度,并基于协定或者其余技术手段,关上实在浏览器,与浏览器中页面交互。
<video id=”video” controls=”” preload=”none” poster=””>
<source id=”mp4″ src=”https://www.cypress.io/static/running-tests-a75997cdc1013fc4b1705c1be3a094c7.webm” type=”video/mp4″>
</video>
端到端测试有着肉眼可见的劣势,比方:<u> 我的项目通过一直的开发,最终必定会趋于稳定,在适当的机会引入端到端测试能及早发现问题,进而保障产品的品质。这种让软件代替人工,施行疾速、重复的测试,收益非常明显。有人总结出端到端收益公式(出处:测试人员的“救命稻草”):</u>
端到端收益 = 迭代次数 全手动执行老本 – 首次自动化老本 – 保护次数 保护老本
即使收益显著,且相干畛域工具层出不穷,但端到端测试的落地施行目前并不宽泛。有条件接入端到端测试的团队,端到端测试仿佛也没有表演到应有的角色,始终难以施展出最大化作用。其起因除了“我的项目特点是否适宜”以外,我认为还和端到端测试在开发上线流程中进行接入的阶段有密不可分的关系。
具体来说,相当多的团队会将端到端测试放到本地执行,和我的项目代码强耦合。比方,本地通过 npm script 脚本来施行端到端测试。端到端测试须要保障最新待测试页面的可拜访性,因而相干脚本须要优先保障本地服务的胜利搭建。以 npm run e2e
这样的 npm script 为例,相干流程如下图:
实质上这是一种“按情绪”执行。个别须要开发者在本地开发结束之后,盲目执行脚本并察看端到端测试后果。“按情绪”的事件,天然是无奈正规化、流程化、平台化的,注定是比拟鸡肋的存在。
顺着上述思路,咱们能够想到:借助 huskey,将上述端到端测试放到 pre-commit 或者 pre-push 阶段强制执行。这样一来咱们使得端到端测试流程化,将“按情绪”执行改为了强制执行。
但进一步思考,在 pre-commit/pre-push 阶段执行的 弊病也很显著:减少了额定的 git hooks,缩短了代码提交流程,间接影响到了上线效率。如果是一个 hot-fix 紧急批改 bug,这样的工夫殇是咱们无奈承受的。同时,本地阶段执行端到端测试的一个前提就是先要保障本地服务的可用性,暂且不谈开启本地服务的工夫开销,一个更难堪问题就在于:本地服务和线上环境是有人造差异的,本地执行的端到端测试难以幂等于线上实在成果。
<u> 基于上述情况,端到端测试在团队技术体系中,要么慢慢地成为一个“丑陋的玩具”,金玉其外; 败絮其中;要么就是一个开发者“眼见心烦的累赘”,最终沦为鸡肋。</u>
这么看来,端到端测试想要破局并冲破,势必应该在执行流程上进行翻新 。为此,咱们认为: 端到端测试应该搬到“容器”上进行,交融到 CI/CD 阶段施行,彻底做到自动化、服务化。
这里插入一个知识点:什么是 CI/CD 阶段呢?
它们都是古代互联网利用编译和公布流程当中的罕用词语,别离对应:继续集成(Continuous Integration)和继续部署(Continuous Deployment),实际上咱们还有一个继续交付(Continuous Delivery)的概念,这里不再具体开展,咱们来聚焦到继续集成和部署。
在继续集成环境中,开发人员将代码提交到骨干 master,并触发 Gitlab 的 hook,进而主动推动代码的编译。不同的团队对继续集成阶段的定义兴许会有稍微不同,然而并不障碍咱们了解。我司在继续集成阶段次要实现:构建我的项目流程。具体来说,在这个阶段中台团队应用根底镜像启动容器,拉取最新代码,装置必要依赖,执行单测脚本,并最终 commit 出下一阶段(继续部署阶段)的镜像。
在继续部署阶段,中台应用构建阶段(继续集成阶段)产出的镜像,启动容器,依照肯定 pipeline 流程,串行公布最新版本利用,最终启动服务。
理解 CI/CD 的概念之后,将端到端测试后置到 CD/CD 阶段,并在实在容器中执行——这仿佛是一个很好的尝试和翻新。
NodeJS 实现端到端服务 —— 想说爱你不容易
截止为此,依据咱们的“容器中执行端到端测试服务并接入 CI/CD”的设计思维,咱们能够画进去一个简略的流程剖析。
由上图,衍生出第一个问题:<u> 咱们应该将端到端服务接入到 CI 阶段还是 CD 阶段呢?</u>
依照常理:
CI 阶段应该器重测试验证后果,以保障所有的提交在部署前的品质,对可能呈现的一些问题进行预警;
CD 阶段应该没有人为干涉,只有当一个批改在工作流 pipeline 中构建失败能力阻止它部署到产品线。
然而,端到端测试须要保障具备一个可拜访的最新版本利用地址,而 CI 阶段在我司只是进行代码的编译和容器根底镜像的生成,并不会开启应用服务,因此不具备端到端测试所需的地址。咱们当然能够“革新”CI 阶段,新启动一个新的过程,进行应用服务的启动,但这样的做法显然粗犷而不合理。
同时 CD 阶段我司在金丝雀过程之前,有一个“办公室阶段”(下文对立应用办公室阶段 / 办公室环境一词),即:办公室内(公司内网环境下)可全量拜访新版本利用。也就是说公司内网下拜访线上地址:https://www.a.com/b,网关会将该流量全部打到曾经部署新版本代码的服务机器上。
综合思考,对于我司来讲,在这个“办公室”阶段,应该是最好执行端到端测试的工夫点。一旦端到端测试无奈通过,将会中断部署交付流程。
这样一来,就解决了全量可测页面的拜访性问题,同时端到端测试的环境齐全和线上环境保持一致。
任何一个创新型我的项目的发展之路,都注定波折崎岖。设计后行,但在施行过程中,咱们还是遇见了较多的阻力和难点。次要问题集中在 端到端框架和中台容器的贴合性、一致性 上。上面我举一些典型例子来进行阐明。
融入社区,反推框架的提高和欠缺
咱们选用了目前业界最为风行和沉闷的 Cypress 作为端到端测试框架,对于不同端到端框架的比照和技术实现原理这里不再赘述,感兴趣的同学能够关注咱们的博文,后续将会专门进行解剖。
整体端到端服务流程并不简单,如下图:
这只是一个极简的图示,粗略地体现了 在相干代码 MR(merge request)胜利构建,并部署到办公室环境时,中台申请咱们的端到端测试服务开启接口。
这就存在了第一个难题:咱们发现,在办公室部署实现之后,端到端服务接管到 post 申请,执行 cypress.run()
,总会失去报错:Cypress binary is not installed。为什么本地就能顺利执行,到了容器上,开启 NodeJS 服务之后就会失去报错呢?
翻看 Cypress 代码实现,究其原因十分乏味,Cypress 会在 npm post-install 过程装置 Cypress binary 到容器零碎门路下,post-install 是 npm 的一个 hook,它会在 npm install 胜利执行后触发。在执行 cypress.run()
时,Cypress 会先执行 cypress.verify()
验证 Cypress 的可用性,其中一个验证规范就是查看零碎门路下是否存在 Cypress binary。
那为什么咱们容器上就找不到 Cypress binary 呢?我仍然用图示还原案发现场:
在第一次构建时,咱们的构建脚本执行 npm install
,并胜利触发 npm post-install,Cypress 将 Cypress binary 装置在容器零碎门路:~/.cache/Cypress
当中。
第二(N)次构建时,面对一个“全新”的空容器,中台为咱们缓存了 node_modules
,因而 npm install
并不会真正下载依赖,post-install 的 hook 也不会触发,也就不存在“Cypress 将 Cypress binary 装置在容器零碎门路:~/.cache/Cypress
当中”这一动作。进而执行时,失去了 Cypress binary is not installed 的报错。
解决方案也不难,我的第一个直观想法是构建脚本当中的 npm install
改为 npm ci
,这里插入一下 npm ci
和 npm install
的区别:
npm ci
须要我的项目必须要含有package-lock.json
或者npm-shrinkwrap.json
文件- 如果上述两种 lock 文件和
package.json
申明的依赖产生抵触,npm ci
命令会强行退出,并报错,而npm install
命令会更新 lock 文件 npm ci
命令会全量装置我的项目所有的依赖,无奈增加独自依赖我的项目- 如果我的项目中曾经存在
node_modules
,npm ci
命令会删除node_modules
文件,并重新安装 npm ci
命令不会写package.json
内容以及 lock 文件内容
因而不难看出,在构建阶段,本就应该应用 npm ci
命令,这也是 npm ci
命令命名的由来。
但应用 npm ci
和中台缓存 node_modules
的行为又相矛盾,无可避免地减少了构建的耗时。在任何公司的构建部署零碎中,npm ci
装置依赖的工夫肯定会是不可疏忽的大头之一。
有没有更“优雅”的办法呢?我深信“<u>PR makes world better</u>”。让咱们回到实质,外围问题在于「中台缓存了 node_modules
,导致 post-install 无奈触发,进而无奈装置 Cypress binary 到容器零碎门路」,如果咱们也能缓存 Cypress binary 到指定门路,且在执行 cypress.run()
以及 cypress.verify()
时,让 Cypress 去设定的缓存门路下找 Cypress binary 是不是就能解决问题。那么这个”缓存门路“当然就是 node_modules
文件下的某个门路即可(因为中台缓存了 node_modules
文件)。
总结一下,关键点在于:
- Cypress 须要新增可配置环境变量,用于指明 Cypress binary 的装置门路
- 咱们设置环境变量 CYPRESS_CACHE_FOLDER 为
./node_modules/.cache/cypress/
cypress.run()
触发cypress.verify()
执行时,去 CYPRESS_CACHE_FOLDER 指定的门路下查找 Cypress binary 是否存在
此时整体流程如图:
对于减少配置环境变量,使得容器环境执行 Cypress 更加灵便的提议,当然也失去了 Cypress 官网的认可,此问题暂告解决。同时,对于 Cypress 自身体积较大,装置耗时且不稳固的问题,咱们同样应用一个 CYPRESS_INSTALL_BINARY 环境变量指明默认的 Cypress 软件下载地址。咱们在公司内网保留一份,内网下载 Cypress 既迅速又牢靠。最终的构建局部脚本如下 (采纳 yml 格局,不影响读者了解):
build:
# export cypress variables
- export CYPRESS_CACHE_FOLDER=node_modules/.cache/Cypress && export CYPRESS_INSTALL_BINARY=http:// 内网地址 /cypress.zip
## application build
- yarn
- yarn build
其中可见在执行依赖装置(yarn)和构建我的项目(yarn build)之前,咱们申明并导出了相干环境变量。
前端和中台化 买通基于 Cypress 的 NodeJS 服务任督二脉
解决了 Cypress binary 装置问题,咱们在执行过程中遇到的第二个问题也很乏味。在 cypress.run()
执行时,失去报错信息,“CI stage dependency missing in docker”,通过和官网团队的探讨:
咱们重大狐疑容器上执行 Cypress 出错的起因在于:容器零碎版本过低。中台以后提供的容器零碎版本均为:Debian 8.2(jessie),NodeJS v10.14.0,即 docker 根底镜像申明为:base_image: nodejs/v10.14.0_jessie (debian 8)。为此,咱们组织中台团队以及公司外部平安组进行沟通,并制作出退出了平安包的新版本 base_image: nodejs/v12.13.0_stretch (debian 9)的根底镜像,供我的项目容器应用。
根底镜像的降级并不是简略制作一个镜像那么简略,其中波及到较多“技术之外”的摸索和磨合,这里咱们不过多开展。总之,中台团队的存在对于各种类型 NodeJS 利用 / 服务的落地和倒退至关重要。同时中台方面波及到的能力是传统前端开发所欠缺的。因而,我的项目推动能力,跨团队沟通能力也是 NodeJS 倒退甚至任何一项前端技术都不可漠视的一环。
此外 Cypress 作为一个简单的端到端测试框架,它自身须要很多零碎级的依赖,比方 Xvfb(is an X server that can run on machines with no display hardware and no physical input devices/ 虚构屏幕虚构输出设施)等,这里梳理总结一下必备零碎依赖包包含:
- xvfb
- libgtk-3-dev
- libnss3
- libxss1
- libasound2
- xz-utils
到此为止,简要总结一下“端到端测试上容器”这一过程遇见的关键问题以及解决方案:
- Cypress binary 装置问题:提 PR 解决,提供 Cypress binary 缓存装置门路
- Cypress 装置超时且不稳固:提 PR 解决,提供内网获取门路,从内网下载
- 容器零碎不兼容:制作镜像并推动中台降级容器零碎根底镜像
当然以上问题并不是全副,但极具代表性,也能总结出任何一个前端团队在公司内推广落地新技术时可能会遇见的问题。具体的挫折可能来自 NodeJS 服务本身,也可能来自于和已有技术体系的不兼容,解决方案有技术方向的致力,也有我的项目推动方向的尝试。
到此,咱们波及了服务粗略设计以及根底环境的搭建。接下来,我将重点介绍一下容器化端到端测试服务的技术体系架构设计。
一个欠缺、易扩大的 NodeJS 服务设计
文章主题围绕着如何开发一个“容器上运行的端到端测试零碎”开展,后面也提到过,其实就是在适合的机会去触发端到端框架的执行,想来就这么简略。然而 咱们在设计一个零碎,一个平台时,应该思考更多问题,比方:
- 横向多我的项目扩大能力
- 平台化服务能力
- 运行效率极致化设计方案
- 通报与预警中断机制
- 正当选型技术计划以及存储计划
咱们的端到端服务起名为「Goalkeeper」,意为“守门员”,心愿它像一名优良的守门员一样,守卫着咱们产品质量的最初一道防线。
横向多我的项目扩大能力
Goalkeeper 目前曾经进入成熟阶段,从立项的角度来说,该 NodeJS 服务不能只服务于一个我的项目测试,现实地它应该具备反对公司内所有产品接入的能力,并将接入过程和复杂度降到最低。
当办公室环境部署实现后,中台申请 Goalkeeper Post 接口 https://api.goalkeeper.com/run
,这个接口提交数据字段包含:
{
"stage_name": "office",
"description": "style: 1221 流动页款式兼容低版本安卓",
"mr_iid": 2049999,
"app_name": "xen",
"author": "houce",
"event_name": "deployment_finished",
"candidate_id": 6666,
"deploy_id": 6666
}
app_name
字段为惟一的项目名称,配合其余表意字段(应该不难理解,这里不再一一阐明),这样的接口设计天然反对全公司所用利用的接入,仅从接口设计上,具备先天扩大能力。接下来的阐明也将进一步就横向扩大来开展。
Goalkeeper 首页仪表盘页面,抉择查看利用我的项目:
运行效率极致化设计
为了最高效地进行端到端测试,咱们剖析:对于不同的利用,应该多核多过程执行端到端测试,保障不同利用测试工作执行的并行性,即对于多个我的项目的部署,端到端测试不会产生阻塞,不排队;对于同一个我的项目利用,必须要防止短时间内屡次不同部署之间的相互影响,对于这些端到端测试执行工作应该正交化设计,串行开展。
具体实施就须要一个音讯队列,不同利用采纳不同音讯队列 tube,雷同利用在同一个 tube 中串行生产和生产。因为 Goalkeeper 是一个服务内的音讯队列设计,因而我选用了轻量且兼具弱小性能的 Beastalkd 作为音讯队列的技术选型。
Goalkeeper 某个利用下部署列表页面,点击查看具体信息:
平台化服务能力
具备了反对多利用的能力,接下来很天然地就想到:「开发者如何查看测试报告和理解测试细节呢」?
Goalkeeper 的设计蕴含了十分重要的一块内容 —— 平台化展现。这其实就是一个典型的:基于 Koa 的 NodeJS 后端服务,前端采纳 React 作为多页面利用计划。具体来说,每次端到端测试服务实现之后,将产生的所有测试报告类数据存入 Redis,开发者拜访 https://www.goalkeeper.com/dashboard
,Koa 基于服务端渲染,获取相干数据进行单页面利用的平台化展现。
这些相干数据,不仅蕴含了每个端到端测试的 case 内容、case 执行信息和后果,还蕴含了测试产生的富媒体文件地址(包含测试录像,测试截图等)。对于测试产生的富媒体文件,咱们采纳了容器长久化技术进行存储,并对外提供动态服务。
换句话说,Goalkeeper 在 NodeJS 的服务层面提供了:
- 容器上的端到端测试
- 整套单页应用服务(包含查问平台和富媒体动态服务等)
比方对于某我的项目利用某次部署测试信息,可查问:
视频信息以及截图信息:
通报与预警中断机制
为了更好地服务线上利用,咱们也设计了高效的通报与预警机制。通报机制是指在一次提交部署开始,对应相干的端到端测试实现之后,通过企业微信和邮件将测试信息和测试平台展现地址发送给提交人或负责人。预警中断机制是指在端到端测试发现异常后果时,阻断上线流程,并强告诉给提交人和负责人。
咱们的异样后果不仅蕴含测试 case 的失败,更具特色的是也蕴含了视觉比对测试(visual testing)的异样。基于 Cypress,咱们封装了一套视觉测试插件,它可能在任意节点主动对测试页面进行全量截图,并保留为比照基准图片。在下一次测试进行时,对以后最新测试的雷同节点进行页面全量截图,并进行和基准图片的比对,如果两幅图片的不同像素超过肯定百分比或肯定像素阈值,则认为视觉比对失败。如图:
视觉比对测试,可能大大解放测试 case 编写的复杂度,非常适合款式类测试的回归。当然对于预期之中的图片比照失败,比方是失常的页面 UI 改版,咱们提供了「跳过视觉比对并更新基准图片」的能力。
在任何一种测试失败时,咱们都会登程预警中断流程。如图,
架构和流程再梳理
咱们通过剖析一个申请的流程,再来总结梳理一下整个设计过程:
如图,简略示例:
更细节一点的图示例:
当开发者提交代码被合并,Merge Request id 为 123 的相干部署到内网环境之后,触发中台 hook,中台会申请端到端测试 Goalkeeper 服务接口 ./run,该服务会为每一个利用创立一个过程解决,利用音讯队列机制跑该次部署的端到端测试,并最初将测试状态后果(running/success/fail)写入 Redis 当中。在这个过程中,中台能够依据轮询接口 ./consult,该接口查问 Redis 中相干 Merge Request id 的测试状态,中台依据该后果值进行解锁上线流程或持续锁定上线。
同时,在该次部署所对应的端到端测试完结时,会更新测试报告平台内容,不便开发者拜访最新部署产生的端到端测试报告以及录像等富媒体信息。相应的告诉和预警机制也会在该阶段触发。
整套零碎的要害依赖的服务项如图:
整个 Goalkeeper 平台次要依附 Koa,Koa-static,Koa-router 来解决测试服务申请,并提供可查问的测试报告平台后盾服务。Cypress 是次要的端到端测试框架,它提供了丰盛的插件和扩大能力,咱们在 Cypress 的根底上,封装了大量贴合本人业务的插件和扩大,比方实现视觉比对测试的 @kfe/goalkeeper-image-snapshot,@kfe/goalkeeper-image-snapshot-runner。Cypress 对应的测试脚本 cases 咱们用一个独自的 Gitlab 仓库保护,每次在部署产生并启动端到端测试时,拉取最新的测试 cases 代码。最初,@kfe/goalkeeper-report-generator 是整个可查问平台的仓库,它是一个残缺的基于 React SSR 的单页面利用,依据 React-router,提供了:
- 首页,仪表盘页面(/dashboard)该页面展现了所有已接入的利用我的项目根本信息
- 利用我的项目详情页(/:app)该页面展现了以后利用我的项目的根本信息
- 我的项目部署列表页(/:app/mrList)该页面展现了以后利用我的项目下,所有的部署信息列表
- 测试详情页(/:app/mrList/:mrId)该页面展现了以后部署对应的测试信息
- 测试媒体查问页面(/:app/:mrId/media)该页面展现了以后部署对应测试的富媒体信息,包含测试录屏、利用截图等
总结
这篇文章咱们介绍了 NodeJS 助力传统端到端测试,最终实现破局和翻新的双赢我的项目案例。具体实施上,文章剖析了如何实现容器上执行测试,如何接入 CI/CD pipeline,如何打造一个面向任何我的项目的 Goalkeeper 平台。
对于前端开发者来说,学习并施行 NodeJS,最要害的就是格局。咱们要熟知 NodeJS 个性,更要有所谓的“后端”思维,架构思维。我置信 NodeJS 的倒退和落地,不是因为它肯定具备了某种与生俱来的力量,而是它的某些特点合乎技术发展趋势或天然更迭法则。
我置信本人,生来如同璀璨的夏日之花,不凋不败,妖冶如火,接受心跳的负荷和呼吸的累赘,乐此不疲。
—— 泰戈尔 Tagore
我想,开发者们肯定会接受心跳的负荷和呼吸的累赘,但对于技术的倒退乐此不疲。
请关注咱们,后续会带来更多前端技术实际和各种常识!
Happy coding!
彩蛋环节
想和我交换更多前端常识,能够关注我的新书:《前端开发外围常识进阶:从夯实根底到冲破瓶颈》。
点赞转发较多的同学,能够收费获取样书!!!
前端畛域从 2013 年左右开始崛起,从 Backbone 到三大框架的此消彼长中,想要梳理出具备体系性的学习要点更难。作者是这个非凡期间的亲历者、实践者和深思者,我在本书中简明扼要地解说了前端应知应会的技巧,使各阶层读者都可从中受害。让咱们与时俱进地开启前端攻城狮难以回避的三十三个外围话题!
内容简介:本书共分 8 局部,涵盖 33 个主题,内容波及 JavaScript 根底强化、JavaScript 语言进阶、不可漠视的 HTML 和 CSS、前端框架、前端工程化、性能优化、编程思维和算法、网络常识等,聚焦前端开发基础知识和进阶技能,关注前端工程化和体系化,构造清晰,循序渐进,深入浅出。
在重构基础知识方面,本书将标准规范和实际代码相结合。在造就进阶技能方面,本书深度分析了技术背地的原理和哲学。书中列举的我的项目设计案例涵盖了许多经典面试题目,不仅能帮忙高级开发者夯实根底,还能为中、高级开发者冲破瓶颈提供帮忙和启发。
作者简介:侯策,曾先后就任于法国 ENGIE 团体、百度等国内外出名互联网企业,具备丰盛的开发教训和团队治理教训。曾负责 GIAC 寰球互联网架构大会演讲嘉宾,FDCon2019 中国前端开发者千人峰会演讲嘉宾。著有《React 状态治理与同构实战》一书。
分割我:LucasHC