前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >基于接口数据变异的App健壮性测试实践

基于接口数据变异的App健壮性测试实践

作者头像
美团技术团队
发布于 2024-03-04 07:09:32
发布于 2024-03-04 07:09:32
2800
举报
文章被收录于专栏:美团技术团队美团技术团队

01 什么是客户端健壮性

在维基百科的定义中,健壮性(Robustness)是指一个计算机系统在执行过程中处理错误,以及算法在遭遇输入、运算等异常时继续正常运行的能力。IEEE中将健壮性定义为系统或组件在存在无效输入或压力环境条件下可以正常运行的程度。早在1989年,Barton Miller首次提出了模糊测试的概念,通过向目标应用抛出随机字符串的方式来测试UNIX应用程序的健壮性;而在1996年的Ballista项目中,研究人员探索根据API定义的数据类型,对操作系统或软件接口进行自动化测试方法。两个项目均以“无应用程序崩溃或挂起”作为测试验证通过的标准。

在移动端App领域,健壮性可以理解为App运行时遭遇环境异常或者输入异常时客户端能够继续正常运行的能力。

其中,环境异常主要分为操作系统异常、外部环境异常、硬件环境异常三大类。比如内存不足、CPU负载过高、线程池满载、内存分配失败、网络连接失败等。输入异常主要分为系统输入和用户输入。比如网络接口返回的数据异常、应用内缓存、数据库文件读写异常,这类的异常属于在系统输入异常;在电话号码输入框场景,用户输入的空格、富文本则属于用户输入异常。

对于这些风险,如果App没有处理,理论上都可能会产生展示异常、交互异常、性能、安全等问题,导致用户无法继续使用或在使用过程中产生不好的体验。比如用户操作App下单过程中,API请求出现故障未返回状态码为200的响应,App由于没有获取到预期接口响应的信息而发生崩溃,就会中断用户的使用流程。

02 基于接口数据变异的App健壮性测试方案设计

在实际的客户端测试执行过程中,测试人员会考虑测试异常输入的场景,但由于成本无法做到无穷尽的测试,同时还存在人工执行遗漏的风险。

从美团App平台业务的历史故障分析中,我们发现:网络请求返回的数据与实现预期不符引发的Crash或核心功能缺失问题导致的故障占比最高,且影响面较广。比如接口返回非预期数据时,客户端处理数据类型转换异常导致闪退,即使5分钟内操作降级仍影响了百万量级的用户。因此美团平台业务App的健壮性测试探索优先从发现网络请求返回数据导致的异常开始。

针对于发现请求接口返回客户端非预期数据导致的Crash,或者核心模块缺失问题这个诉求,我们调研后发现方案的基本原理都是相似的,即以网络请求的原始响应为基础,根据规则进行变异构造,使用代理工具改写响应体返回给客户端,在端上设备做异常检测。但是都存在一些问题不能满足诉求,比如测试变异数据是根据预置或者自定义规则随机生成组合,随机性过大,不能有效拦截健壮性问题;但如果不做随机,产生的用例组合量过大,测试不能在合理时间范围内结束;另外在检测能力方面,不具备发现业务异常或功能模块异常的能力。

因此,我们结合通用方案做了一些自定义改造,整体检测方案包含静态检测和动态检测两部分。

  • 静态检测,主要是指静态代码扫描,将典型代码编写规范问题转化为自定义静态代码扫描规则,管控增量代码,同时长期治理存量风险。比如自定义了PrimitiveParseDetector、ColorParseDetector,管控业务必须使用健壮性测试通过的工具类。
  • 动态检测,是指结合触发时机,构造并注入变异数据后,识别App运行时是否出现崩溃、挂起或业务功能模块异常。比如在集成事件/回归事件触发自动化测试运行,构造触发异常的数据进行动态测试,然后监测是否出现了异常。核心动作包含构造变异数据和完成检测两部分。比如将接口响应体中表示颜色含义的Key对应的Value值构造成非色值,然后检测客户端请求处理接口数据时是否出现崩溃或挂起。

下文重点介绍端到端的动态检测方案。

03 变异数据的构造和异常检测

对于美团App来说,首页有多种形态,对于某种特定形态,除了控制请求数据外还需要控制实验、策略等一系列因素,才能保证测试对象的唯一性。一个页面中包含多个异步请求,因此请求的构造也需要和页面路径关联。这些都是采集变异所需的基础数据时需要关注和控制的。

响应体由基本类型数据和复合类型数据组成,相同基本类型的数据可能具备不同的业务语义,需要根据语义的类型做变异规则的区分对待,才能保障业务场景覆盖。

因此,如何保障变异数据构造的全面性和准确性,是我们面临的首要挑战。

要解决数据构造全面性问题,首先要解决页面描述方案,这样才能控制获取基础数据的唯一性。在解决方案中,我们构建了页面描述的特征规则,解决用户视角的页面标识问题。需要的信息包含端信息、页面路由信息、实验策略账号信息、页面标识模块合集等。通过页面请求数据自动录制的方式,自动更新迭代请求数据和页面之间的绑定关系,使得基础数据能够随需求迭代更新,从而通过变异规则构造生成的用例也能够自动更新。

在用例变异生成构造上,对于响应体里的Value设置了语义匹配规则,比如字符串的语义可能代表颜色、页面跳转路由、动静态资源链接(即图片资源数据/视频文件/GIF文件),需要区分特征分别按语义构造异常数据。

比如在图片的变异数据构造里,除了需要构造非图片链接情况外,还要考虑不同图片格式、非图片格式以及非合法的图片剪裁格式拼接等场景。

我们对接口返回数据使用脚本做了初步的语义分析,人工二次校正后建立了基本数据类型和语义的映射集合,结合基本数据类型边界值和语义定义了初始的变异规则。然后对历史的线上健壮性问题和线下测试发现的健壮性Bug的变异数据进行整理,作为增补的变异规则。

在自动化测试执行过程中,我们基于App可测性改造提供的能力,对测试场景进行了控制,同时基于布局视图的解析SDK、App异常上报SDK提供的能力,完成了对App异常的通用检测。

04 变异数据的精简方案

伴随着变异规则的丰富,自动生成的数据量级是巨大的,数据的变异组合如果按照全覆盖方式来生成组合数量就是指数级增长。比如对于1种有7种变异取值的变量,如果存在n个此类型变量,就会产生7^n种数据组合,并且在实际业务场景中很多组合情况是没有意义的。

如何在保障用例构造全面性的情况下精简变异构造的用例数,是我们面临的第二个挑战。解决方案包含2个策略:1)数组元素结构一致时,删减构造的用例数;2)结构不完全一致的数组元素,引入编辑距离和并查集算法判断节点相似性,节点不相似,可以在一次数据生成里做合并构造

我们可以把请求响应的JSON理解成树,第一个解决思路是判断树中节点、路径的相似度,相似节点删减构造。

如果路径、节点相似,可以推测路径即业务逻辑也是一致的,比如页面上的一些列表元素,可能是数据结构对象完全一致数组,如果对每个数组对象中的每个元素进行全用例构造,生成的变异数据量极大,且对业务场景或代码逻辑的增量覆盖有限,因此我们决定将构造逻辑优化,进行删减构造。即假如数组中元素的结构完全一致,那么同含义的字段可以为他们分配不同的变异构造值,然后删减掉无效的构造情况。应用这种方法可以有效降低28%左右的用例构造数量。

如图数组的3个元素中均存在“resourceName”键值对,假如每个键值对有3种变异取值,按照全排列方式进行用例构造将会生成有9份变异数据,在删减构造情况下,可以分别为它们构造一个特定的变异值,这样变异生成用例数量可以从9减少为1。

在对业务接口返回数据的数据结构进行分析后,我们发现在层级越深的场景下,距离根节点越近的两个节点,业务逻辑耦合和结构相似程度越低,它可以进行合并构造,相互逻辑之间不会产生影响,比如有两个键值对,每个键值对的Value有3种变异取值,在合并构造情况下,可以从排列组合的6份数据减少到3份数据。

基于这个思路,我们在实践中引入了编辑距离和并查集算法,以节点路径为参照,对树的每一层的每两个节点计算编辑距离,生成一个n*n矩阵;同时以树的高度减去节点位于的层数作为权重,修正编辑距离。基于这样的计算,会产生多个编辑距离矩阵。

为了尝试最大化合并构造用例效果,我们把编辑距离做了0,1矩阵转化。其中,由于编辑距离为1的两个节点可能存在业务逻辑耦合关系,必须放在同一个组里分别构造,所以我们把编辑距离大于1的情况转化成了0,最后得到了一个0,1的编辑距离矩阵。

在0,1矩阵情况下,我们使用了图的连通性概念,如果A和B连通,B和C连通,那我们认为A和C连通,转化到这里的概念就是A和B相似,B和C相似,那么A和C相似,它们应该被放在同一个组里分开进行构造,那么在同层元素构造时,我们会从每个分组里取到一个节点,对这些规则进行变异组合构造。

基于以上两个策略进行精简后生成的变异数据量较精简前降低了40%,同时代码覆盖率没有明显变化,并且保持不变的健壮性问题发现能力。

美团App和优选App都接入了这个工具,在新需求阶段可以人工触发运行,还可以结合客户端组件集成事件和回归事件做自动触发。至今应用一年时间内,发现了几十个问题。

05 总结及展望

在健壮性工具建设一期里,我们实现了App页面加载展示场景的健壮性问题检测,支持崩溃、卡死和部分功能异常这三类异常检测。另外,基于节点相似性优化变异数据生成策略能够在保持效果不变的情况下有效控制测试时长,但是否有更优的合并算法和推荐算法,还需要更多的尝试。

在后续工具的迭代还会继续围绕异常构造和异常检测这两个方向,支持更丰富的构造能力和检测能力,以及更高效的构造效率。

短期建设上,我们将会从业务视角出发丰富自动化变异数据生成建模,完善客户端异常通用异常检测能力,完成通用前后端交互的数据构造类型(比如:长连接消息)的覆盖;长期建设上,需要支持更丰富的数据和环境构造能力,通过智能化用例生成,提升测试效率。

06 Q&A

Q1:节点相似的判断依据是什么?

A:从实际的response分析来说,两个节点的路径完全相似就是从根节点到最终的叶子节点上,它们的路径命名完全相似,数组里两个对象的结构完全一样。

Q2:用例的生成能举个例子吗?

A:比如颜色色值的格式是#+6位字符,通常运营配置会出现的情况是忘记添加#,或色值复制中少了一位。在这种情况下,我们会构造一个色值,比如没有返回#、色值位数不对、色值添加透明度,把这种场景作为构造情况,在配置里添加上,最后用代码生成。

Q3:健壮性平时执行的频率是什么样的?

A:第一个基于需求维度,需求维度需要人工触发;第二个基于变更维度,当组件发生变更时,可以关联到这段代码或者组件变更的页面,然后触发页面对应的健壮性测试,执行频率会受到组件变更频率的影响;第三个在回归测试时,App的回归测试两周一次,我们会把所有页面以及它关联的所有的用例都执行一次。

Q4:对于暴露给前端开发的接口,大部分是人为调用参数的变化,随机性相对比较高,对于必填和非必填参数如何确认用例的范围?

A:目前我们在实现的方案里,没有区分参数是必填参数还是非必填参数,所以对于整个数据接口返回里的所有结果都会进行构造,产生的问题是对于非必返回的参数可能产生的问题,到底是否是需要解决的问题,这部分目前通过运营手段做确认。

Q5:首页可能调用10个接口,然后针对每个字段都进行异常验证吗?

A:对于首页关联的接口,我们在接口请求、录制过程中和录制完数据后,会对接口进行确认到底有哪些接口是我们需要验证的,这是一次性的成本,录制完成后,会对每个字段都进行异常验证,当然会有一些黑白名单的设置。

Q6:对色号这种情况有一种生成规则嘛,这个规则是怎么制定?

A:刚刚我只是举了一个色号的例子,其实对于图片、请求的资源文件、配置文件、跳转链接,每一个对应到的业务语义,我们都有对应的用例生成规则,我们会根据参考依据,比如第一个是本身我们在通用的基础库里怎么处理这些问题,这里有一个基础的规则;第二个是我们积累了线上问题情况实际可能会产生的错误或者变异情况,生成第一版基础规则,在第一期工具里找相关研发达成共识,这样的话,数据变异是处于合理范围。

Q7:执行的时候,如何知道页面对应哪些规则提前配置?

A:执行时,在测试接入过程中有一个配置过程,它不是配置这个页面和接口的关联关系,而是配置我们要测试哪些页面,自动触发自动化录制过程,就是到这个页面时,会触发哪些接口请求,生成这个页面和这个接口请求的对应关系,给到对应的配置人做确认,保证哪些接口是真正可能想要构造的,哪些接口不需要构造,最后以这个为基准测试,基于录制过程,比如业务迭代里面产生了新接口,我们在录制中能够感知到它关联的接口发生了变化,在发生变化时发消息给对应的测试提交人/负责人,TA确认这条规则放到黑名单里还是更新到需要构造的接口里。

Q8:是否有做页面显示的一个校验?怎么做的?

A:目前我们在页面里的模块做了“是否展示”校验,基于当前集成到美团的可测性SDK,这个SDK会获取到当前页面是否渲染里是否展示了对应模块的信息,通过请求把对应模块描述传给SDK,通过返回来校验是否展示。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-02-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 美团技术团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Rust入坑指南:智能指针
在了解了Rust中的所有权、所有权借用、生命周期这些概念后,相信各位坑友对Rust已经有了比较深刻的认识了,今天又是一个连环坑,我们一起来把智能指针刨出来,一探究竟。
Jackeyzhe
2020/03/12
9030
【Rust 基础篇】Rust 智能指针
在 Rust 中,智能指针是一种提供了额外功能的指针类型。智能指针可以在编译时和运行时检查内存安全,并提供了更灵活的所有权和借用模型。本篇博客将详细介绍 Rust 中的智能指针,包括常用的智能指针类型、创建和使用智能指针、内存安全和性能考虑等。
繁依Fanyi
2023/10/12
2710
【Rust精彩blog】Rust 中几个智能指针的异同与使用场景
想必写过 C 的程序员对指针都会有一种复杂的情感,与内存相处的过程中可以说是成也指针,败也指针。一不小心又越界访问了,一不小心又读到了内存里的脏数据,一不小心多线程读写数据又不一致了……我知道讲到这肯定会有人觉得“出这种问题还不是因为你菜”云云,但是有一句话说得好:“自由的代价就是需要时刻保持警惕”。
MikeLoveRust
2020/02/20
1.9K0
2023学习日志
此时可以使用Box<T>指针指向嵌套的列表,得到cons list类型的结构体。(指针的内存大小是已知的,但列表的大小是在进行结构体声明时未知的)
TomoriNao
2023/07/27
1650
【Rust每周一知】理解智能指针Box<T>
指针是个通用概念,它表示内存地址这种类型,其引用或“指向”其他数据。Rust中的指针是“第一类公民”(first-class values),可以将它们移动或复制,存储到数据结构中并从函数中返回。Rust提供了多种类型的指针:
MikeLoveRust
2020/02/20
2.2K0
rust的内存管理
内存管理是rust最有意思的事情了。rust的内存管理有三条准则。 let分配资源 分配会转移所有权,比如赋值直接move了 值和变量在作用域末尾会被清理,释放 drop方法会在释放前调用 rust支持移动语义和复制语义,为此抽象出了两个trait,clone和copy 非堆内存可以使用copy,隐式转化,clone需要显示调用 关于借用的规则,使用& 一个引用的生命周期不能超过其被引用的时间 如果存在一个可变借用,不允许存在其他值 如果不存在可变借用,允许存在多个不可变借用 借用规则方法类型 &self
李子健
2022/05/08
7620
【译】Rust与智能指针
如果你一直在订阅这个系列,关于所有权的那篇文章[1]可能给你带来了这种印象——Rust 确实是个好东西,C++不应该在生产环境中使用。智能指针可能会改变你的想法。用现代的话来说,Smart pointers 是指那些有点(嗯......)额外(东西)的指针。他们本质上还是管理其所指向的对象的内存地址,并且当对象不再被使用的时候会将其释放。这消除了很多因不恰当的内存管理而引起的 bug,并使得编程不再那么枯燥乏味。C++智能指针为原始指针提供了一个安全的替代方案,而 Rust 智能指针则在保证安全的前提下扩展了语言功能。
MikeLoveRust
2020/10/26
1.1K0
【译】Rust与智能指针
Rust 总结
所有权是用来管理堆上内存的一种方式,在编译阶段就可以追踪堆内存的分配和释放,不会对程序的运行期造成任何性能上的损失。
谛听
2022/06/04
1.8K0
揭开智能指针 Box 的神秘面纱
熟悉 c++ 的肯定知道 shared_ptr, unique_ptr, 而 Rust 也有智能指针 Box, Rc, Arc, RefCell 等等,本文分享 Box 底层实现
MikeLoveRust
2021/08/13
6150
《Rust避坑式入门》第1章:挖数据竞争大坑的滥用可变性
赵可菲是一名Java程序员,一直在维护一个有十多年历史的老旧系统。这个系统即将被淘汰,代码质量也很差,每次上线都会出现很多bug,她不得不加班修复。公司给了她3个月的内部转岗期,如果转不出去就会被裁员。她得知公司可能会用Rust重写很多系统,于是就报名参加了公司的Rust培训,希望能够转型。
程序员吾真本
2024/08/29
5810
《Rust避坑式入门》第1章:挖数据竞争大坑的滥用可变性
Rust学习笔记Day17 智能指针之Box<T>
经过这一段时间的学习,基础知识里,我们还剩数据结构没有学习,而数据结构里最难的就是智能指针。
用户1072003
2023/02/23
3760
Rust学习笔记Day17 智能指针之Box<T>
Rust学习笔记Day18 智能指针Cow/MutexGuard
这是用于提供写时克隆(Clone-on-Write)的一个智能指针,和虚拟内存管理的写时复制很像。
用户1072003
2023/02/23
7180
Rust学习笔记Day18 智能指针Cow/MutexGuard
Rust编程学习笔记Day7-一个值可以有多个所有者吗?
我们之前介绍的单一所有权,其实已经能满足我们使用内存的大部分场景。在编译时就能完成静态检查,不会影响运行时的效率。
用户1072003
2023/02/23
9890
Rust编程学习笔记Day7-一个值可以有多个所有者吗?
66个让你对Rust又爱又恨的场景之一:变量与值
属于手动内存管理流派的C++,虽然提供了手动管理内存的灵活性,但容易因程序员的失误导致内存泄漏、悬垂指针、双重释放和野指针等问题。
程序员吾真本
2024/07/18
5560
66个让你对Rust又爱又恨的场景之一:变量与值
【Rust 基础篇】Rust 的 `Rc<RefCell<T>>` - 共享可变性的智能指针
在 Rust 中,Rc<RefCell<T>> 是一种组合智能指针,用于实现多所有权共享可变数据。Rc 允许多个所有者共享相同的数据,而 RefCell 允许在有多个引用的情况下对数据进行可变操作。
繁依Fanyi
2023/10/12
9470
31.Rust-智能指针
Rust 可以在 堆 上存储数据。Rust 语言中的某些类型,如 向量 Vector 和 字符串对象 String 默认就是把数据存储在 堆 上的。
面向加薪学习
2022/06/30
3210
go 开发者的 rust 入门
即:在任意给定时间,要么 只能有一个可变引用,要么 只能有多个不可变引用。引用必须总是有效的。
王磊-字节跳动
2021/11/27
1.9K0
【Rust每周一知】如何理解Rust的默认线程安全?
本文以Rc和RefCell为例,讨论Rust中的Send和Sync是如何保证线程安全的。
MikeLoveRust
2020/02/12
1.5K0
《Rust避坑式入门》第2章:解决多线程并发数据竞争的不可变性
从第1章所讨论的出现数据竞争问题的多线程并发剧院订票系统的代码能够看出,虽然可变性能够方便地随时修改值,但滥用可变性,会在多线程并发编程时,带来数据竞争的难题。
程序员吾真本
2024/09/03
7170
《Rust避坑式入门》第2章:解决多线程并发数据竞争的不可变性
Rust避坑现代C++悬垂指针
C++是一门应用广泛的编程语言。在2023年JetBrains全球开发者生态问卷调查中,C++在受访程序员过去一年中的使用率,占25%,紧跟JavaScript、Python和Java之后。在本书撰写时,根据JetBrains的统计,程序员使用最多的是C++17。
程序员吾真本
2024/09/18
6442
Rust避坑现代C++悬垂指针
推荐阅读
相关推荐
Rust入坑指南:智能指针
更多 >
LV.1
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档