一、背景介绍
日常的迭代或者技术改造之后,系统常常会出现一些功能丢失、新增接口权限和页面白屏等问题。尽管事后可以依靠监控大盘查看监控数据来定位问题,但是这些手段是滞后的,无法提前发现系统已有问题。
为了保障系统的稳定性和强壮性,常见的方案包括代码层面覆盖单元测试,保证已有代码的强壮性,如前端平台商家组已经推行单元测试用例覆盖,但是也存在以下问题:
- 单元测试测试颗粒度较小
- 大部分单侧逻辑局限于函数级别的验证,难以覆盖整体流程
除了单元测试保障之外,系统集成测试在保障质量方面也起着重要作用。然而,集成测试同样面临时间成本高、遗漏问题、重复性测试等挑战。特别是在功能全量回归场景下, 测试用例数量庞大,手动回归测试变得非常繁琐。尽管业界已经有一些成熟的自动化回归集成方案,例如通过录制手段来自动生成测试用例,但这种方案仍然存在一些挑战:
- 改变研发流程具有挑战性:研发团队通常会专注于新功能的开发和问题修复,所以增加测试用例录制的环节可能会对时间和资源带来压力。
- 用例维护成本较高:在敏捷迭代中,页面UI频繁变动和核心链路不断更新迭代, 录制生成的测试用例可能在下一次迭代中变得不再适用或无法执行,意味着测试用例需要进行不断的更新和维护。
确实,为了应对迭代变更带来的挑战,的确需要寻找更灵活有效的自动化回归方案。我们研发了一款创新的测试工具—Monkey Testing(简称Monkey)。本文将深入阐述Monkey的出发点、创新过程以及落地情况,探讨其可行性和价值。
二、Monkey诞生
Monkey , 也有人叫做搞怪测试,一般指用毫无规律的指令或操作去测试被测系统,观察被测系统的稳定性。
出发点
基于Monkey自动化平台与前端平台B端大仓组件的配套研究方向,围绕B端单品组件ProTable、ProForm自动化测试可行性验证。首先模拟真实用户行为,通过数据填充,模拟不同类型的数据输入,并通过Click事件触发条件搜索, 来达到验证接口异常场景下的健壮性的目的。同时,我们将通过劫持请求、捕获JS执行上下文错误、网页截图判定白屏等方式,发现潜在的稳定性问题,从而提高系统的稳定性。
我们提供低成本的自动化回归方案,需要具有以下效果:
- 测试流程的自动化,降低测试成本同时提高准确性和覆盖率。
- 线上问题前置化,及早现版本可能存在白屏、接口请求响应异常、JavaScript运行时报错等常见的通用问题,保障系统在线上环境的稳定性。
核心能力介绍
Monkey端侧自动化能力
图片
Monkey平台化能力建设
图片
Monkey自动化测试核心流程
图片
上述Monkey自动化测试实现,主要分为以下四个阶段:
- 准备阶段:在这个阶段,首先需要进行B端SSO系统的免登,以确保Monkey能够正常登录系统。同时还需要获取天网菜单数据源,以便后续的操作可以正确地找到对应的页面和元素。
- 执行阶段:在这个阶段,使用Puppeteer提供的客户端浏览器环境,搭载随机测试脚本,模拟用户的行为操作(简称Monkey Runner)。通过这种方式,Monkey执行ProTable、ProForm测试模型用例,包括点击、输入、选择等,以覆盖不同的测试场景。
- 数据劫持:在执行阶段下的操作行为中,Puppeteer会捕获异常情况。这些异常可能包括页面加载失败、元素找不到等问题。通过捕获这些异常,可以及时发现并处理潜在的问题,以提高系统稳定性。
- 数据清洗:在这个阶段,基于Node服务实现数据上报和清洗。Monkey会将执行阶段下的操作行为数据上报给后台服务,后台服务可以对这些数据进行分析和处理,以生成测试报告、统计指标等。同时,也可以对数据进行清洗,以去除无效或冗余的信息,保持数据的准确性和可用性。
三、Monkey创新过程
智能模拟用户行为
Monkey通过模拟真实用户的随机操作(例如点击、滚动、输入等)来测试应用程序的输入输出是否具有稳定性和健壮性,以此来发现应用程序中的问题和异常行为,例如崩溃、卡死、界面异常等等,覆盖系统各个功能模块,发现潜在问题。
图片
Monkey以其简单粗暴的方式随机生成用户输入来模拟各种用户行为,具有以下特点:
- 高覆盖率:通过随机输入的方式,能够触发系统中各种不同操作。
- 发现意料之外的异常:由于随机性,Monkey 能够发现一些预料之外的异常情况,帮助提前发现潜在问题。
尽管Monkey有很大的优势,但也存在一些局限性:
- 准确性和可重复性受限:由于随机生成输入的特性,Monkey 无法保证测试的准确性和可重复性,可能导致某些特定场景下的行为无法被模拟。
- 无法模拟特定场景下的用户行为:由于随机性导致无法模拟特定用户行为和上下文环境,不利于测试特定场景下的交互和功能。
为了收敛Monkey 随机性,达到更加精细化执行测试目的 ,我们将探索Monkey与B端组件二相互结合的可行性测试。
Monkey结合B端组件测试
结合大仓组件能解决什么问题
大仓组件旨在解决前端平台应用中的交互一致性和可复用性问题。结合大仓组件,我们可以将组件固化的操作行为拆解为多个测试步骤,每个步骤对应多条测试用例,以确保每个组件都具有各自的交互流程。在Monkey执行过程中的用例脚本阶段,能够准确模拟用户行为,从而消除Monkey测试中的随机性。
结合大仓组件实践
01ProTable单品类组件行为分析&拆解实战
图片
操作行为分析
对ProTable组件的操作行为进行分析时,可以着重考虑以下方面:
- 表格数据加载:用户打开包含ProTable组件页面时,首先会加载数据并显示在表格,数据源通过接口请求获取。
- 条件筛选和重置:对ProTable进行条件筛选以便查看数据,也可以重置筛选条件以恢复初始状态。
- 表格分页查询:使用分页器进行翻页等操作,以便展示更多的数据内容。
拆解步骤
以ProTable组件为例,可以将组件的操作行为拆解成以下步骤:
步骤1:组件加载
- 测试用例:通过DOM Class 或者ID查询判定是否存在元素。
步骤2:筛选和搜索和重置
- 测试用例: 验证表格的搜索功能是否正确响应用户输入,并能正确筛选和显示符合条件的数据。
- 测试用例:模拟点击重置按钮,并确保表单数据被成功重置为默认值。
边界:这块我们能做的是模拟用户输入行为按钮点击触发搜索。
步骤3:分页切换
- 测试用例:点击页面切换按钮或输入页码,验证表格切换 调用接口有无异常。
边界:这块我们能做的是 模拟点击页码行为 劫持请求是否异常。
步骤4:数据列渲染
- 测试用例:请求到数据,列表元素正常渲染。
注意事项
安全区域操作约束在测试环境中,一些删除或更改操作可能会导致测试数据的不一致性或损坏,因此需要对测试环境中的点击行为进行约束,确保在安全区域内进行操作。
// 检查一个元素是否在另外一个元素中 原理类似于JQ $("#container").has(".selector");
const isSafeArea = (parentElement, childElement) => {
const childClassNames = Array.from(childElement.classList);
return childClassNames.every(className => parentElement.querySelector(`.${className}`));
};
const layoutContentElement = document.querySelector('.layout-content');
const isisSafeAreaResult = isSafeArea(layoutContentElement,element)
优化导航栏点击许多B端系统中,导航栏是一个常见的元素。为了更加精确地进行测试并提高效率,我们需要规避导航栏的点击或者A链接点击跳转,专注于当前页面的操作。
element.matches('a')
图片
02ProForm单品类组件行为分析&拆解实战
操作行为分析
对ProForm组件的操作行为进行分析时,可以着重考虑以下方面:
- 数据加载或关闭:通过点击入口按钮显示弹窗,弹窗加载表单项:当用户完成操作后点击关闭按钮,弹窗关闭。
- 表单操作:在弹窗表单项中可以进行数据输入、选择选项、上传文件等操作。
- 数据校验:填写完表单数据后,ProForm组件会对表单数据进行校验,确保输入数据符合规定的格式要求。
- 数据提交:填写完表单数据后,通过点击提交按钮将数据提交到后端接口进行保存或更新。
拆解步骤
以ProForm组件为例,可以将组件的操作行为拆解成以下步骤:
步骤1:组件加载
- 测试用例:通过DOM Class 或者ID查询 判定是否存在元素。
步骤2:表单输入填充
- 测试用例:表单项渐入数据
- 测试用例:验证表格的搜索功能是否正确响应用户输入,并能正确筛选和显示符合条件的数据。
- 测试用例:模拟点击重置按钮,并确保表单数据被成功重置为默认值。
边界:开发FormFilter 函数尽量满足通用的场景下的数据渐入能力;目前文件上传场景、下拉联动场景、远程搜索场景未覆盖。
步骤3:表单验证
- 测试用例:验证输入字段是否满足预期的格式,如邮箱地址格式、密码强度等。
- 测试用例:验证表单的必填字段是否被正确地标记,并且不能提交空值。
边界:Monkey本身不具备带有规则限制表单填充能力;数据来源于ProTable页面数据和Input Types类型数据填充。
步骤4:表单提交
- 测试用例:模拟点击提交按钮拦截接口是有异常。
步骤5:表单取消
- 测试用例:模拟点击重置按钮,并确保表单数据被成功关闭。
ProForm准确提交率
衡量标准:通过劫持接口请求来判断Proform是否成功提交,并且服务端接口是否返回状态码200。
指标说明:
- 前端准确提交率:确保表单项数据填充完整且点击提交按钮能触发提交接口(填写的数据能够通过前端校验)
- 业务准确提交率:前端准确提交基础上,要求提交接口返回的HTTP状态码为200且bsCode为200(填写的数据能够通过服务端校验)
统计过程如下:
图片
- 为每个巡检的页面 URL 分配唯一的 UID,并记录 UID 与 Page URL 的映射关系。
- 当页面弹出新增弹窗时,填充完数据并点击提交,记录事件类型为 event_type = 'CLICK',同时记录时间戳和 UID。
- 劫持接口数据,并记录事件类型为 event_type = 'RESPONSE'。
- 对 event_type = 'RESPONSE' 的时间戳进行分组统计,确保大于 event_type = 'CLICK' 的时间戳。
根据当前数据统计,前端准确提交率约为60%+,绕过服务端校验准确提交率约为20%+。
图片
表单项数据填充
表单项数据填充主要有2种类型,分别为Input types填充和劫持表格数据源实现精准填充。
图片
01表单项 - Input types 通用填充
B端系统未接入类型检测工具, 接口请求参数类型约束不严格。Monkey 会根据 Input Types 类型随机生成字符串,填充表单项以触发接口调用,发现接口参数类型错误。
1. Input types 类型穷举表单项的填充方式(MDN输入元素):文本类型(input type="text")、数据类型 (input type="number")、邮箱类型 (input type="email")、日期类型(input type="date")、单选框 类型(input type="radio")、复选框类型(input type="checkbox")
图片
图片
// input type 集合
const defaultMapElements = {
textarea: fillTextAreaElement,
'input[type="text"]': fillTextElement,
'input[type="password"]': fillTextElement,
'input[type="number"]': fillNumberElement,
select: fillSelect,
'input[type="radio"]': fillRadio,
'input[type="checkbox"]': fillCheckbox,
'input[type="email"]': fillEmail,
'input:not([type])': fillTextElement,
};
// 填充逻辑
const fillTextElement = async (element, character) => {
const selectedCharacters = Array.from({ length: 5 }, () =>randomizer.getCharacter[Math.floor(Math.random() * randomizer.getCharacter.length)]).join('');
const newValue = element.value + (character ? character : selectedCharacters);
if (element) {
triggerSimulatedOnChange(element, newValue, window.HTMLInputElement.prototype);
return newValue;
}
};
// Hacky function to trigger react, angular & vue.js onChange on input
const triggerSimulatedOnChange = (element, newValue, prototype) => {
const lastValue = element.value;
element.value = newValue;
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;
nativeInputValueSetter.call(element, newValue);
const event = new Event('input', { bubbles: true }) as CustomEvent;
// React 15
event.simulated = true;
// React >= 16
let tracker = element._valueTracker;
if (tracker) {
tracker.setValue(lastValue);
}
element.dispatchEvent(event);
};
2. 接口参数类型错误
案例:后台页面Barcode条件筛选系统异常。
归因:前端未限制Barcode只能输入Number类型;服务端用Long类型字段接收BarCode字段,随机输入了String类型的值时,系统出现异常。
- 场景 - 服务端接口参数未校验
图片
- 场景 - 服务端接口参数已校验
图片
图片
图片
02表单项 - 精准填充
图片
说明:在 ProTable 筛选和 ProForm 表单项数据填充场景下,希望数据填充能够精确匹配,表单项的数据源来自于 ProTable 表格数据。统计该数据是为了衡量表格数据与ProTable & ProForm 输入项的匹配率指标,该指标为辅助性指标。指标的数值越高,ProTable场景代表覆盖筛选场景越多;Proform场景代表表单校验验证通过率越高。根据目前的数据统计,整体匹配率占比约为 50%+左右。
实时验证前后端接口参数一致性
案例:后台页面筛选条件返回结果错误。
归因:接口字段改动,前端接口请求参数字段名错误。
为解决服务端接口字段调整导致前端无法及时感知接口参数变化的问题,我们拟计划接入文档接口平台开放API。通过查询接口API获取请求参数、响应参数及其类型,以验证前后端接口参数的一致性。若接口正常但文档定义存在问题,可反向约束规范接口定义文档流程。
图片
自动化错误捕获
通过劫持请求、捕获JS执行上下文错误、网页截图判定白屏等方式,来快速检测页面故障。
错误类型分类
图片
常规错误捕获
除了上述捕获的错误问题,还列举些其他常见错误。这里我们通过场景模拟,验证Monkey的错误捕获能力。
1. 模拟接口请求404场景 - 劫持请求检测
图片
2. 模拟白屏场景 - 白屏检测
3. 模拟JS 执行上下文异常 - 劫持JSError检测
用例模型定制化配置
在用例管理的前期阶段,我们将首先支持系统生成的通用类型用例。这些通用类型用例是根据业务需求在开发过程中编写的,涵盖B端系统的主要功能和常见场景。经过验证和测试后,这些用例可以作为基准用例使用。
为了满足不同场景下的测试需求,将逐步开放定制能力。为用户提供文档和示例、可视化配置界面以及插件化扩展机制,降低用户编写用例的难度和上手成本。具体内容如下:
- 文档和示例:提供详细的文档和示例,帮助用户理解如何配置Monkey的用例模型,以及如何利用上述的配置文件、可视化界面和扩展机制来定制Monkey的行为。这样可以让用户更容易上手,并且了解如何利用Monkey的定制化设置来满足不同的测试需求。
- 可视化配置界面:为了提高易用性,可以开发一个可视化的配置界面,让用户可以通过图形化界面来配置Monkey的行为模式和参数,而不需要直接编辑配置文件。这样的界面可以提供简单的拖拽、下拉菜单等操作,让用户能够直观地配置Monkey的行为。
- 插件化扩展机制:为了未来开放给用户并支持更多定制化需求,设计一个插件化的扩展机制,让用户可以编写自定义的插件来扩展Monkey的行为模式。这样的扩展机制可以提供接口和文档,让用户能够编写自定义的行为模式、触发条件等,从而实现更灵活的定制化设置。
四、Monkey可行性探索情况
我们成功接入了《运营后台系统》项目,并顺利完成了流程。经过数据收集和清洗等步骤,产出了数据分析报告。
数据分析
图片
图片
有效错误率统计
图片
注:在每次任务中,错误数量保持相对稳定的水平。
五、总结 & 规划
Monkey作为一种创新的测试工具,为系统稳定性的保障提供了新的思路和方法。通过结合B端组件可行性探索,我们发现Monkey在实际项目中具有良好的应用前景和价值。根据数据分析结果,Monkey在系统稳定性的提升方面展现出了巨大的潜力。通过对有效错误率、分类错误项和优化方案的分析,我们发现Monkey可以帮助识别各种类型的错误,从而提升系统的稳定性和可靠性。这些数据清晰地展示了Monkey在实际项目中的应用前景和价值,预示着Monkey将为系统稳定性的提升带来新的可能性。
在未来的优化计划中,我们将重点关注以下几个方面:
- 优化现有组件的数据匹配准确率。
- 组件品类的扩充,扩大支持的组件品类,覆盖更广泛的测试场景。
- 定制化配置用例模型,旨在提高错误类型识别的准确性。
- 提高系统的运行效率,计划整合CI/CD流程。