您的位置:  首页 > 技术杂谈 > 正文

飞书 Tech Lead 解析制胜法宝 - 集成测试工程实践

2022-07-12 15:00 https://my.oschina.net/koderover/blog/5552627 Zadig云原生交付 次阅读 条评论

Zadig 推荐语:“先进团队,先用飞书“,在激烈竞争的协同办公市场,飞书以一匹黑马的姿态杀入,并在短期内收获了“好用”的评价,被众多优秀的企业采用。随着飞书产品的演进和成熟,背后的质量工程也挑战重重,经过多年的实践和探索,他们沉淀了一套面向音视频领域独特的建设思路和宝贵的实践经验,Enjoy!

笔者介绍:吴国华,曾在微软 BingAds 从事 SRE 相关工作,专注研发效能、集成测试、容灾稳定性等相关领域。现在字节跳动飞书视频会议团队担任 Tech Leader,负责 SIP 业务线的技术架构、稳定性和质量保障等相关领域。

业务介绍

作为早期加入飞书视频会议(VideoConference,简称 VC)团队的员工,我基本见证了飞书视频会议产品从 0 到 1 的发展过程。飞书视频会议团队成立于 2018 年的年中,在成立之始,我们眼前就已经有像Zoom、Webex 这样的大山。想要在这个领域创新,是极为不易的。

但幸运的是,飞书的优势更在于一体化的套件。“套件战胜单品”是我们重要的战略思路,飞书拥有 IM、文档、日历、邮件、审批、People、百科、小程序、视频会议等等丰富多彩的功能模块,贯穿企业办公的方方面面。也许单看某一项功能,飞书可能不如竞品,但作为套件整体来说,飞书拥有极为流畅的一体化体验:例如飞书文档中可以分享飞书 IM 群组、飞书视频会议中可以直接投屏飞书文档、飞书日历中可以预约飞书视频会议等等。

由此可以看出,飞书的定位是“All in One”全家桶,我们的目标或者说愿景是成为企业办公套件领域的 No.1。

随着飞书自身的向上发展,套件各个模块之间可以互相借力,出色的模块又可以反向带动飞书继续向上发展,形成良性循环。因此在这样的基础之上,虽然挑战很大,但我们依旧对从 0 到 1 开始做飞书视频会议产品抱有信心。

综上所述,基于飞书整体的战略思路,我们秉承“先快后稳”的产品路线。时至今日,我们陆续创立了:飞书会议、飞书会议室、飞书SIP/H.323会议室连接器、飞书妙记、飞书直播等等音视频产品矩阵。

体系建设

回顾业务产品发展的历史进程,可以总结为三个大阶段:快速迭代、鼓励创新、专注品质

  • 快速迭代(初创期):我们从将 VC 集成入飞书(即飞书会议)着手开始探索并快速迭代功能。这个时期虽然人员、技术、流程发展迅速,但整体处于初级阶段,因此质量 baseline 较低。
  • 鼓励创新(产品突破期):我们希望通过创新来弯道超车竞品,因此陆续开启多项探索型工作。例如:飞书会议独立 App、飞书妙享、飞书妙记、会议 AI 特效、字幕等等。与此同时,专职 QA 团队成立,技术、流程、规范都陆续趋于成熟,质量最终能够提升。但始终缺乏对线上变更的实时检测能力,导致质量忽高忽低,波动明显
  • 专注品质(商业化期):我们基本完成了目标客户的大部分卡点需求,因此品质成为重点,对不发生严重事故的提出高要求。也是在这个时候,我们引入了端到端自动化测试(即本文想要重点介绍的)、音视频品质测试、各机型硬件兼容性测试等等手段,质量稳步提升。

抽象地说,如果以质量作为评价标准,那么,我们在这三大阶段的趋势大致如下:

而经过长期的探索,我们也建立以流程、数据、测试为核心的质量保障体系,本文重点介绍测试工程:

漫谈测试

首先我想强调:测试是手段,质量才是目标

很多人认为测试 = 测试自动化,或者认为只有测试自动化了才能保障质量,这是一种比较常见的误区,忽略了测试自动化本身的成本。做业务的同学应该能理解,时间是影响业务成功的重要因素,我们不能一味追求测试自动化而忽视产品争取市场的关键节点,这是典型的技术驱动产品。事实上,飞书包括字节整体都有相当数量的 QA,手动测试和自动化测试我们同样看重

另一个比较普遍的误区是当一个团队有专职的 QA 之后,大家会习惯性将所有与测试相关的责任全部归在 QA 身上,这是一种工程上的短视。相信大家都听过 DevOps 这个词,DevOps 是一种文化,涵盖了软件交付生命周期的全流程,让研发人员和运维人员观念达成一致,打破所谓的职责边界。对测试的态度同样如此,优秀的工程师会把开发思维、测试思维、运维思维贯彻始终,而不是纠结谁的职责是什么。

其次我想强调:测试是多元化的

多元化的第一个体现是上面提到的:测试不应局限于自动化,手动测试一样是重要手段。而且,并不是所有业务场景都能做测试自动化。以 VC 为例,很多特定型号的第三方硬件终端并不提供 OpenAPI 的支持,因此无法进行自动化,这种情况下手动测试就是唯一的选择。

多元化的第二个体现是:测试的粒度。相信大家都听过单元测试、集成测试、系统测试等等名词,这里面表述的是对不同测试粒度的称呼。一般来说,测试粒度越细,测试用例就越依赖于打桩(指代以 mock 为主的技术手段,来模拟外部依赖的返回值),测试稳定性就越好,但相对的测试仿真程度就越低。随着测试粒度不断提升,带来的收益就是效果会越接近于用户的真实体验,但测试稳定性会越差,成本也会越高。不同粒度的测试侧重点不同,它们之间不是互斥的,事实上它们通常会同时存在于系统中。

多元化的第三个体现是:测试的阶段。对新功能的测试和对现有功能的测试思路是不一样的,尤其当全部的测试集比较庞大的时候。通常来说对于新功能,我们认为所有场景的用例都需要覆盖至少一次;而对于现有功能的测试,我们则会在日常回归中覆盖主场景。复杂的系统需要对测试用例进行分级,以 VC为例,我们根据不同的业务场景重要性将测试全集划分为 P0/P1/P2/P3 等级别,日常回归重点覆盖P0/P1。总之,不同的测试阶段重点不同,测试成本也不同,我们需要结合实际情况来运用不同的方法。

明确目标

正因为测试是多元化的,测试最终目的是保障质量。所以我们更加需要了解所在的环境下目前缺少的是什么,我们希望做一套什么样的测试实践,我们的手段是手动还是自动化,我们主要针对的测试环境、测试粒度和测试阶段是什么。即我们需要一个清晰可描述可达的目标

那么回到这里,为什么我们会想到做这样一套端到端自动化测试呢?

我们的原始动机是因为缺乏对线上变更的实时检测能力。更具体地说,我们曾一度因为突发的变更,如:动态配置变更、基础设施变更、机房网络抖动等等原因,发生过严重事故。虽然这些变更也会触发各自的监控告警,但我们更希望从用户的角度来看实际的影响。

因此从这个基点出发,我们想到的是引入一套常驻的循环测试机制(PS:所以可以看出不是手动而是自动化),模拟视频会议的用户行为,来保障不发生严重事故。

当我跟别人谈起要做一套针对飞书VC端到端自动化测试的时候,不少人上来就问:“那么你用的是什么测试框架?GoConvey 还是 Ginkgo?(PS:字节服务端主要是 Go 技术栈)”,这样的问题其实看似简单直白,但问题的答案对于理解这套测试实践其实没有太大帮助。比起这个,更重要的是这套测试实践针对什么场景。

因为我们不是要设计一套自动化测试框架去统一世界,然后不惜一切代价去替换其它不同维度的自动化测试

经过一段时间的调研,我们开始逐步明确新框架的定位。

首先思考的是测试粒度。我在字节跳动飞书为什么选择 Zadig 实现主干开发、主干发布 一文中曾经介绍了我们在单元测试和微服务集成测试的思考实践,因此当下我们缺乏的是粒度更粗更接近用户真实体验的端到端测试,即从音视频客户端到音视频服务端全链路的测试。

其次思考的是测试内容。在视频会议的业务场景下,连通品质是两个完全不同的测试方向。用直白的话来说,连通指代的是“听不见、看不见”即未成功加入房间,品质指代的是“听不清楚、画面模糊”即加入房间但体验不顺。由于品质差的直接原因 90% 以上是终端网络环境差,所以在严重程度上,我们会更高优去看连通问题。我们经过反复讨论之后,决定主要针对连通问题来设计框架,而品质测试则引入专业的音视频实验室设备(例如:网损仪)来推进。

第三个重要思考是测试客户端。不同于模拟 API 请求直接发送给服务端的测试方式,当我们考虑端到端全链路的时候,我们需要重点考虑客户端的技术实现。这里面的争议点主要在于是直接用真机测试还是用虚拟进程测试。从仿真程度来看,无疑是直接用真机更好,但真机的机型繁多,购买真机成本很高,无法满足大方数会议的测试场景。另一个真机的致命问题是稳定性较差,真机不比虚拟进程,往往还依赖各硬件自身的稳定性,尤其是当真机处于网络条件差的情况下会有五花八门的问题扑面而来。因此,我们在慎重的决策之后,选择基于虚拟进程,即虚拟客户端来进行测试。

第四个重要思考是测试环境和测试阶段。从我们创建这套测试的动机来看,我们需要直接对生产环境发起测试,同时 7x24 小时无间断重复性触发。关于直接对着生产环境测试这一点,可能会引发一些争议。所以我在这里澄清一下,飞书有租户的概念,各租户之间在生产环境是隔离的,我们可以创建测试租户来落地自动化测试。

总结一下,在经过一系列的思考讨论之后,我们对新框架的定位基本明确:这是一套端到端全链路的、针对连通性、基于虚拟进程、直接面向生产环境、7x24 小时无间断的测试实践。

框架设计

当目标明确到清晰可达之后,我们就开始着手设计测试框架,最终的框架设计结构大概如下:

  • Automation 平台:基于开源组件 Zadig 搭建的一套集成测试平台,用于提供定时执行测试集、隔离不同测试集环境、测试集结果展示与分析、测试集失败通知与告警等等能力
  • E2E 测试框架:基于 Go 语言自研的 E2E 测试框架,对测试开发提供友好的封装与抽象,简化测试用例编写与维护
    • AutomationAPI:E2E 测试框架封装的原子 API,控制终端设备行为
    • AutomationAPICallback:E2E 测试框架封装的原子 APICallback,终端设备产生的结果
  • Automation 中心服务:运行在核心机房的控制中心,集中转发 API 和 Callback
  • Automation Worker:运行在边缘机房的工作节点,管理并创建虚拟客户端,与中心服务交互 API 和Callback

概括来说,这套框架核心在于:可以大规模动态创建 VC 虚拟客户端,且虚拟客户端能够被 API 化,测试结果能够被 Callback 化

服务架构如下:

AutomationAPIAutomationAPICallback 是整套测试实践中的两个核心抽象概念:API 是对 VC 客户端控制指令的抽象,例如:入会、发布音视频流、订阅音视频流、麦克风静音/解除、摄像头打开/关闭等等用户动作;APICallback 则是触发用户动作之后,会议房间产生的事件,例如:创建会议事件、接入网关事件、入会事件、推流事件、拉流事件、离会事件等等。

我们在框架层提供了对 API 和 APICallback 的封装,暴露给测试开发更加贴近业务、更加友好的调用方式。测试开发只需要在测试用例中组合 API,观测 APICallback,对各个阶段进行断言,即可完成测试。

框架实践

经过一段时间的开发之后,我们完成了框架的实现,下面分享一些实现的核心要点。

AutomationE2E && Zadig

从架构上面可以看出,AutomationE2E 是我们针对音视频场景自研的 Test Executor,底层直接基于原生的 Go Test。结合之前的经验,我们把整套 E2E 放在 Zadig 上面。

首先在 Zadig 上面创建测试:

创建好测试之后,我们又将它关联到了工作流上面:

接着打开触发器,根据一轮测试运行的大致时长来设置定时器,保障整体近似 7x24 小时无间断:

可以看出,只是经过非常简单的几个步骤之后,Zadig 就为我们提供了强大的基础保障

当测试失败,我们可以通过 Zadig 快速下载到相关日志,并帮助研发定位问题,体验非常好

总的来说,我们可以通过 Zadig 的数据面板来看一下测试收益:

AutomationAPI

每个测试用例本质上是一系列 API 的组合,控制远端虚拟设备完成入会/离会等用户行为。因此,AutomationAPI 本质上是一个网关层,用于提供远程控制的网络通道。

AutomationAPI 主要提供 gRPC 协议接口。值得一提的是,我们基于 gRPC 的流模式来完成服务端推送,因为 APICallback 是作为异步事件形式产生的。

AutomationBackbone

Backbone 是整个测试框架的实际后端核心,将必要的数据(例如:记录测试产生的设备、记录测试的结果等等)持久化到 DB。

除此之外,Backbone 另一个核心的作用是作为中心统一管理各 Agent,在并行测试过程中根据 Agent 的负载和地理位置调度合适的 Agent 实例。

AutomationAgent Agent 是实际的工作节点,最大作用是创建虚拟设备

虚拟设备成本远低于真机,一台 32Core64GB 配置的云主机可以模拟 100 个 VC 参会者,而如果是 100个真机参会者需要花费的成本是上百倍的。

因此,我们将支撑音视频核心能力的 SDK 封装成虚拟设备,运行在云主机上面。不同的业务产品所需要的虚拟设备不同,我们主要支持了 RTC 虚拟设备(应用于飞书会议)和 SIP 虚拟设备(应用于飞书 SIP/H.323会议室连接器)。

我们为 Agent 定义了通用的接口,由不同的虚拟设备自身来实现这些接口,让 Agent 能够同时支持不同协议的虚拟设备。

实践经验总结

回顾整个过程的测试工程实践,我这里也想总结分享一些比较容易被忽视的点。

测试稳定性

当集成测试的粒度越接近用户真实体验,稳定性就越容易劣化。我们在实践过程中发现测试稳定性甚至是决定整套测试实践生死的重要指标!相信做过集成测试的同学都有感受,集成测试很难像单元测试一样每次运行都很稳定

我们认为只有当集成测试对业务具备强大威慑力的时候,才是集成测试最有价值的体现,即所有研发会对集成测试的失败当做第一优先级去处理。

那么想要让框架达到这一点,我们就需要不断优化框架自身的稳定性。我们需要让研发看到的是:测试的失败原因绝大部分都是业务侧原因,而不是测试框架自身原因。否则长久之后,测试的失败就不会再有人关心了。

我们经过了非常长时间的优化,基本做到框架稳定性 SLA 在 99.9%:

测试并行化

测试集运行周期越短,测试覆盖的轮次就越多,对业务的保障效果就好。那么怎么让测试集运行周期足够短呢?

我们的实践是测试并行化。测试用例之间必须保证互相无依赖,即一个 TestCase 产生的数据不能用于另一个 TestCase 的输入,这样我们就可以假设测试之间可以独立运行。

以我们在 Zadig 上面实际运行的例子来说:一共 1326 个测试用例,每个测试用例都至少运行 20s 以上,多的甚至有 80s 以上,但整体持续时间是 12m12s,这里面就是测试并行化起到的核心作用。

测试归因分析 当测试失败的时候,我们希望最小化研发定位问题的额外成本。针对这一点我们主要做了两方面的努力。

第一方面是将失败测试进行聚类分析。(PS:这一点与音视频业务强相关,音视频场景下经常因为同一个原因引发大量的失败)

第二方面是针对每个失败的用例,快速提供日志下载。

总结感想

以上就是我们这套测试工程实践的关键思考过程和核心实现要点,由于保密原则,不方便更加细节地展开,望谅解。同时希望能够通过我们的多年实践,引发大家对于测试工程的深度思考,能够结合自身业务特点产生更多有价值的实践

Zadig,让工程师更专注创造。

Zadig on Github
Zadig on Gitee

展开阅读全文
  • 0
    感动
  • 0
    路过
  • 0
    高兴
  • 0
    难过
  • 0
    搞笑
  • 0
    无聊
  • 0
    愤怒
  • 0
    同情
热度排行
友情链接