腾讯文档EP之路 —CI x 自动化测试

2020-09-10 14:10:10 浏览数 (1)

点击上方蓝字关注我们!

| 导语  持续集成强调开发人员提交了新代码之后,立刻进行构建、测试。根据测试结果,确定新代码和原有代码能否正确地集成在一起。本文介绍了腾讯文档项目中自动化测试在持续集成中的实践。

背景

  1. 腾讯文档自动化测试种类较多。包括了:单元测试,bvt测试,集成测试(包括了基于接口输入输出进行验证的端到端测试和Web端API接口测试),e2e测试(UI触发UI验证的界面自动化测试)以及性能测试。
  2. 测试代码编写语言,使用框架种类较多。由于大部分前端测试框架单元测试与e2e测试相互独立,所以会导致前端e2e测试形式的多样性。以腾讯文档为例,单元测试与bvt测试基于ts/js,使用jest框架;集成测试基于puppeteer,使用jest框架;e2e测试与性能测试基于python,使用公司内的qta框架。
  3. 测试用例与测试代码割裂。测试用例由excel表格编写,完成测试用例评审,然后导入到某个用例管理平台;测试代码编写者在测试代码仓库编写测试代码,然后将测试代码位置回填到用例管理平台。
  4. EPC标准下持续集成流水线包括了:提交构建流水线,定时流水线。还有MR测试流水线。每种流水线都包含不同类型的自动化测试进行校验。提交构建流水线包括单元测试与bvt测试,对有新的合入的master分支进行检测;MR测试流水线与定时流水线包括了单元测试,集成测试,e2e测试。其中,MR测试流水线对发起MR的分支进行检测,定时流水线检测master分支,并将测试数据上报到各epc平台。
  5. epc标准下的自动化测试要求覆盖率满足要求,且要求开发同学参与非单元自动化测试的编写与维护,测试类型偏向于引导白盒测试。
  6. 腾讯文档项目分为excel,word,ppt,收集表四个品类,分别对应四个git仓库,属于四个不同的web项目,每个项目都需要单独建立持续集成流水线。
面临的问题
流水线
  1.  流程配置繁琐。因为需要在每个品类的蓝盾仓库中分别创建所需流水线,流水线中都需配置自动化测试。导致在建设流水线时太多重复:需要在流水线中建立每个自动化测试模块,然后将每个自动化测试模块分别添加进每条流水线。

每个品类的蓝盾仓库中都需配置持续集成流水线

每条流水线中都需配置自动化测试模块

  1. 维护,统计成本高。当某个自动化测试模块发生变更时,需要去不同项目,不同流水线下同步更新。当需要统计自动化测试数据时,也需要去不同项目下的不同流水线中获取数据。
  2. 覆盖率的计算复杂。尤其是增量覆盖率的计算与非单元测试覆盖率的计算,需要额外的工具支持,没有统一测试框架集成。
自动化测试
  1. 编写门槛高。测试代码开发语言、使用框架较多;非单元测试使用非开发语言编写,对开发同学来说会造成额外的学习成本;不利于快速上手,有抵触。

基于QTA框架使用Python编写测试代码

  1. 用例管理复杂。借助第三方平台,人为关联用例,操作复杂,容易遗漏。且无法实现测试代码与测试用例的版本控制。
  2. 不利于白盒测试。非单元测试由测试同学完成,测试同学不清楚代码内部逻辑结构,只能进行UI触发UI验证的界面自动化测试,或基于接口输入输出进行验证的端到端测试。
  3. 覆盖率低。测试同学只关注需求功能点,导致测试用例重合度高,而不能保证所有开发代码的链路逻辑被检测。
  4. 接口必须挂载。测试代码与开发代码割裂,导致测试代码必须依赖于浏览器环境进行,导致逻辑层接口必须全部挂载才能被测试同学使用到。
  5. 检测手段单一。只能检测浏览器加载的dom节点,导致整个检测链路很长,且对于一些需求根本无法检测。

面向CI的自动化测试设计

基于以上背景信息,为满足epc标准下的持续集成与自动化测试要求,我们做了面向CI的自动化测试设计:

自动化测试同源

开发代码与测试代码同源,指的是开发代码与测试代码使用相同语言编写,放在同一仓库,开发代码与测试代码统一管理:

  1. 提供一套能执行各种自动化测试类型的,统一的前端测试框架;
  2. 测试代码使用js/ts语言编写,集成到开发代码仓库;
  3. 提供测试用例管理功能;
自动化测试模块整合

虽然腾讯文档项目分为4个品类,每个品类都需要不同的持续集成流水线。但是每条流水线中包含的测试模块基本是相同的。即只需要通过品类与流水线类型这两个关键字,就能得到所需的自动化测试模块。

整合思路
  1. 自动化测试可以按是否依赖环境分为两类:一类是不需要依赖环境,如单元测试与集成测试中的代码长链路测试;另一类是依赖环境的测试,如e2e测试和集成测试中的接口测试。不需依赖环境的测试可以在拉取代码后直接进行测试,而依赖环境的测试需要等待环境部署。
  2. 每种流水线存在差异:MR测试流水线需要统计新增代码的覆盖率,定时测试流水线需要统计整个测试代码的覆盖信息与通过率信息,并且需要将数据上报到各平台。因此每个自动化测试模块在不同的流水线中有细微差别。
  3. 不同品类的测试代码在执行前的环境准备不同。因此同一流水线模版中各品类的自动化应该区分。
原子化流水线

原子流水线

将各品类中CI流水线具有通用功能的部分进行抽取,封装成一个个功能单一的原子流水线。如单元测试流水线,自动化测试流水线,编译部署流水线,通知流水线等。

主流水线

在每个单独品类项目的蓝盾流水线仓库中分别建立提交构建,次级构建与定时构建主流水线,每条主流水线中只划出待执行的节点,串联起整个CI流程。每个节点通过调用子流水线的形式完成节点功能。

覆盖率

EPC标准要求自动化测试与与单元测试的增量和全量行覆盖率满足对应level指标。为达到目标:

  1. 在mr流水线时,对发起mr的分支设置增量覆盖率红线,不满足增量覆盖率要求的分支不予合并,保证了增量行覆盖率的要求;
  2. 在定时构建流水线中加入覆盖率平台的插件,将全量行覆盖率数据报给覆盖率平台。
覆盖率计算
  1. 单元测试覆盖率:单元测试的覆盖率信息可以通过jest框架直接获取;
  2. 非单元测试覆盖率:非单元测试相较于单元测试,不同点在于需要通过对开发代码进行插桩获取js执行信息,然后将信息post到执行统计的服务器上,通过服务器获取覆盖率信息。
  3. 增量覆盖率:获取分支与主干的diff信息,然后与覆盖率信息合并,推送增量覆盖率信息。
覆盖率红线

单元测试会产生覆盖率文件,非单元测试也会产生覆盖率文件。只要单元测试,或非单元测试,或两者合并能满足增量覆盖率要求,即认为满足红线要求。这里的难点在于:

  1. 覆盖率信息如何通讯。这里包含了三个覆盖率:单元测试覆盖率,非单元测试覆盖率,单元测试与非单元测试总覆盖率,只要有一个满足红线标准即可。但是,由于单元测试与非单元测试在不同的子流水线上,且蓝盾暂时没有流水线之间通信的能力,所以并不能在某条流水线上同时获取到这三个信息。
  2. 单元测试与非单元测试的覆盖率文件合并。

腾讯文档实践

自动化测试
dwt(devops web test)解决方案

dwt是一款腾讯内面向Devops的web同源自动化测试解决方案,提供了丰富的测试生态。dwt-testend是dwt框架提供的一套用ts语言编写,用于Web应用程序测试的工具。集成了selenium,puppeteer等主流前端应用测试工具,并提供了一套统一的API服务。

测试-开发代码同源

基于dwt与dwt-testend,所有的自动化测试类型可以使用一套框架完成,并集成到开发代码git仓库中:

  1. 统一框架、统一语言,极大的降低了开发同学参与测试代码编写,尤其是非单元测试代码编写的门槛;
  2. 测试代码与开发代码同步提交,回滚。使测试代码也能实现版本控制与回溯;
  3. 提高测试覆盖率,提高白盒测试比例。能够清楚的知道单元测试与集成测试接口所包含的开发代码逻辑,对于遗漏的地方可以迅速补全。同时对与开发代码逻辑中的重难点进行更多测试;
  4. 全链路检测。以前开发只完成单测,保证了单个函数内所有分支的有效性;测试同学只关心功能是否满足要求,对开发代码的整条链路逻辑却没有覆盖;
  5. 多样化的检测手段。除了检测浏览器加载的dom节点,还能直接检测逻辑层输入输出,或通过jsdom等模块检查某一组件点。
测试用例管理
  1. 使用yaml文件描述测试用例。通过在yaml文件中定义一系列的描述字段来表示一个测试用例;
  2. 将表示测试用例的yaml文件放入开发代码中同一管理。测试用例与测试代码同源,可以通过自定义字段或目录结构自动关联;与开发代码统一自动管理,使测试用例也能实现版本控制与回溯;
  3. 通过解析yaml文件,可以自动化的生成各种文档,方便用例评审;也可以根据测试用例自动化的生成测试代码框架,加快测试代码编写;
测试代码目录结构

测试代码同统一放在根目录「test」文件夹下,然后再创建「unit」「integration」「e2e」「testcase」四个个子文件夹分别表示单元、集成、e2e测试与测试用例。

  • 「unit」与「integration」文件夹(这里的integration指的是不依赖于浏览器环境,关注于开发代码整条逻辑链路的集成测试代码)下目录层级与开发代码目录层级保持一致,结构清晰,方便查找;
  • 「integration」文件夹(这里指的是挂载在浏览器环境下的接口测试)下分为「testcase」与「interface」两个文件夹。两个文件夹下的目录层级相同,对应开发功能模块。「testcase」文件夹下放置的是集成测试代码,「interface」文件夹下放置的是封装的接口。两个文件夹下层级目录保持一致,类似于单测与开发代码一一对应的关系;按照开发功能模块划分,使接口封装完成后能清晰的找到对应的目录,避免了之前由测试同学凭主观判断,按照功能分类,导致对同一模块下的代码进行检测,却被分类到了不同的测试目录,或者检测两个毫无关系的开发模块的测试代码却放在了同一文件夹的问题;
  • 「e2e」文件夹下分为「logic」、「locate」、「testcase」三个文件夹。「logic」文件夹中封装了腾讯文档UI操作的基础方法,「locate」下根据UI界面分为了不同的文件,每个文件对应UI界面中的某一部分,每个文件中包含该部分界面中元素的xpath或 css路径,「testcase」下按照需求功能分为了不同的文件夹,每个文件夹下包含了对该功能的测试用例。
  • 「testcase」文件夹下主要包含了测试用例信息
 执行耗时

   1.执行机器

蓝盾提供了Docker on VM/DeCloud的公有构建机,但是在使用过程中发现如下问题:

  1. 构建机构建时间长,且容易构建失败;
  2. 公有构建机上执行速度慢;

通过指定私有构建机可以解决这个问题;

   2.测试用例并行执行

jest框架提供了“--maxWorkers”参数来控制执行测试用例的最大线程数(the maximum number of workers the worker-pool will spawn for running tests)。通过指定该参数,可以提高在执行机上测试用例的并行执行数量。

流水线
自动化测试子流水线模板
  1. 将测试模块分为两类:单元测试(不依赖环境),非单元测试(依赖环境)。
  2. 将腾讯文档同类型测试模块按品类区分放在同一条流水线中,通过传入的品类名来判断进行哪个品类的测试模块;
  3. 将附加功能(覆盖率红线、上报插件等)加入流水线中,通过传入的流水线种类来判断附加功能是否执行;

通过对自动化测试模块的整合,在一条流水线中配置了所有品类的自动化测试模块,避免了需要在不同的项目下不同流水线中重复建立自动化测试模块。同时,各品类的所有自动化测试数据都可以在这条流水线中获取;对不同品类自动化测试的更新与维护也都在一条流水线中完成。

原子化流水线

在每个单独品类项目的蓝盾流水线仓库中分别建立提交构建,次级构建与定时流水线,每条流水线中只划出待执行的节点,每个节点通过调用子流水线的形式完成节点功能。

覆盖率
1.覆盖率计算

单元测试

腾讯文档使用jest框架进行单元测试,单元测试结束后会生成覆盖率文件。通过在单元测试前比较发起mr的分支与master分支获取diff信息,然后对比生成的覆盖率文件,计算出增量覆盖率,然后与覆盖率红线对比。

自动化测试

上述的自动化测试覆盖率计算方式可以解决覆盖率的统计问题,但实际使用中发现主要会有以下几个问题:

  1. 自动化用例要等postcoverage这一步返回才结束,有时数据量很大会很耗时。大幅增加了执行时间。
  2. 过程中要通过post进行数据传递,并通过get获取计算信息。对于、多种网络策略的公司往往需要在不同类型网络服务器上部署多套服务兼容多种网络类型的服务器。
  3. 所有项目的计算都在一台服务器上,该服务器计算压力大。

为解决上述问题,目前的处理方案:

  1. 执行非单元测试时,在每个测试用例最后增加自动下载原始覆盖率json文件到本地的步骤;
  2. 将本地下载的覆盖率文件通过覆盖率平台插件上传;
  3. 获取覆盖率平台计算结果

2. 覆盖率红线

  1. 覆盖率信息通讯问题。如果将MR流水线不做成原子流水线,所有步骤均在一条流水线中完成的确可以解决这个问题,但是方式及其不优雅,相当于之前流水线建设的努力白费了。解决思路是指定执行机,单元测试与非单元测试均在一台私有构建机上完成。通过指定目录,即可归档单元测试与非单元的数据,这样就能解决流水线之间的通信问题。
  2. 覆盖率文件合并问题。本质上解决了通信问题,能够拿到覆盖率文件,通过对比diff信息与覆盖数据能够很容易的拿到总覆盖率信息。
  3. 覆盖率红线设置问题。由于蓝盾暂不支持红线之间的“或”关系,所以暂时通过bash脚本来模拟流水线质量红线的设置。

后续计划

集成更好的开发模式

 TDD(Test-Driven Development) 测试驱动开发

测试驱动开发的核心在于先写测试程序,然后编码实现功能。

先写测试程序,就要求在开发前完成了测试用例的设计、评审;要求在开发之前有了整体技术设计与技术评审,能提前设计好待实现的函数,接口等。这与EPC中对自动化测试与测试管理的要求需有极大的相关性。

腾讯文档项目中已经有部分需求在实践TDD研发模式,后续计划会打造自动化的TDD流程。

DDT(Data-Driven Test) 数据驱动测试

数据驱动测试,即相同的测试脚本使用不同的测试数据来执行,测试数据和测试行为进行了完全的分离。

数据驱动测试的优势在于利用模型化的设计,避免重复脚本,减少建立和维护脚本的成本;同时输入数据,结果数据,测试脚本分开,有利于测试同学的更改与维护;

以腾讯文档对复制粘贴功能的检查为例,输入的数据包括了数据样式,格式等差异的各种文本,通过数据驱动测试,检测脚本与数据分开,一套脚本既能检测所有数据。

BDD(Behavior-Driven Development) 行为驱动开发

行为驱动开发的核心在于提供了一种通用的,简单的,结构化的描述语言对项目进行描述,使项目成员之间非常顺畅的沟通。

通过结构化的语言,能够断言一些预期的行为,并根据这些行为,完成相应的代码实现。使开发与测试相互独立,在开发时就能完成测试代码的编写。

界面化的操作

目前子流水线的操控方式不友好,新品类接入、新同学接手难度高:

  1. bash脚本太多。依赖安装,测试执行,红线数据获取,...,都是通过bash脚本的方式集成在流水线中;
  2. 插件太多。eptest平台,tcm平台,覆盖率平台,...
  3. 子流水线还是较多。提供了提交构建流水线,定时流水线,MR测试流水线三条子流水线,而每条子流水线中只是某些附加功能需不需要。

初步设想:

  1. 提供蓝盾流水线插件。在插件中配置测试执行命令,将所需数据在插件中吐出;通过勾选或变量控制的方式决定附加功能是否开启,决定向哪些平台提供数据;
覆盖率

目前单元测试的覆盖率已经集成到dwt测试框架中,但是非单元测试的覆盖率还主要是通过bash脚本或平台插件才能获取到。后续会计划将非单元测试的覆盖也集成到dwt测试框架中。

end

扫描二维码获

取更多精彩干货

注:图片均来源于网络,无法联系到版权持有者。如有侵权,请联系后台做删除处理。

0 人点赞