本文介绍的文档“practical gui testing of android applications via model abstraction and refinement”发表于2019年的第41届IEEE/ACM国际软件工程会议(International Conference on Software Engineering, ICSE),由来自加州大学戴维斯分校、南京大学、佐治亚理工学院和苏黎世联邦理工学院的多个团队共同完成。该研究提出并实现了一种名为APE的、新颖且完全自动化的Android应用GUI测试方法。
本研究属于软件工程领域,具体针对移动应用(尤其是Android应用)的自动化测试。Android应用的图形用户界面(Graphical User Interface, GUI)测试传统上严重依赖人工编写测试脚本,不仅耗时耗力,且难以适应频繁的UI变更。为了解决这一问题,学术界和工业界提出了多种自动化测试技术,主要分为模型无关(Model-free)和基于模型(Model-based)两大类。
模型无关的典型代表是Google开发的Monkey,它通过随机生成事件进行测试,但缺乏引导,难以覆盖深层功能且难以避免无效操作。而基于模型的测试方法(如STOAT、AMOLA等)通过构建一个表示应用状态的模型(通常为有限状态机)来引导测试。这类方法的关键在于“状态抽象”(state abstraction),即将具体的GUI视图(views)和动作(actions)映射为模型中的抽象状态和动作,以控制状态爆炸问题。然而,现有基于模型的工具普遍采用静态抽象(static abstraction),即在测试开始前预设好固定的抽象规则(例如,忽略列表视图中子项的所有属性或仅保留索引)。这种静态抽象存在固有缺陷: 1. 过度粗粒度抽象:可能将行为不同的GUI动作映射到同一个模型动作,导致测试时无法准确回放模型动作序列(即选择的具体GUI动作产生了预期之外的行为),进而误导后续测试。 2. 过度细粒度抽象:虽然精确,但会导致模型状态急剧膨胀,增加探索负担,难以规模化。
因此,如何设计一个能够平衡模型大小(size)和模型精度(precision)的动态抽象机制,是提高Android应用GUI测试有效性的核心挑战。本研究的目标正是解决这一挑战,提出一种能够在测试过程中动态优化模型抽象的新方法。
研究团队将所提方法实现在一个名为APE(可能意为“Android Practical testing via model Evolution”)的实用工具中。APE的核心创新在于“动态模型抽象”(dynamic model abstraction),其工作流程是一个持续的模型构建与优化的循环,主要包括以下关键环节:
APE将测试模型定义为四元组(S, A, T, L): * S:状态集合。每个状态是一个属性路径(attribute path)的集合,代表了抽象后的GUI树。 * A:模型动作集合。每个模型动作是一个属性路径,代表了抽象后的GUI动作。 * T:状态转移集合。记录了从一个状态通过一个模型动作到达另一个状态的关系。 * L:抽象函数,是将一个完整的属性路径(full attribute path,代表一个具体的GUI控件及其位置)映射为一个简化后属性路径的核心函数。APE的创新性体现在如何表示和优化L。
其中,抽象函数 L 被设计为一个决策树(decision tree)。树中的每个节点是一个“约简器”(reducer),它定义了如何截取和保留原始控件路径中的属性(如索引、文本、类型)。每条边是一个“选择器”(selector),决定了哪些控件路径会被引导至该分支下的节点进行处理。这种决策树表示法具有表达能力强、易于动态调整、且对人类可解释(方便集成用户自定义规则)的优点。
APE的测试循环始于一个默认的、可能比较粗糙的抽象函数(例如,模拟STOAT的静态抽象)。在每次测试迭代中,它执行以下步骤: 1. 从被测应用捕获当前GUI树。 2. 使用当前的抽象函数L,将GUI树映射为当前模型状态s’,并将上一步执行的模型动作π和上一状态s组合成状态转移(s, π, s’),更新到模型M中。 3. 触发优化过程:针对新状态s’和新转移(s, π, s’),检查是否违反模型质量要求,并尝试优化抽象函数L。优化由三个子过程驱动: * 动作精化(Action Refinement):如果一个模型动作在当前GUI树中对应了过多(超过预设阈值α,默认为3)的具体GUI动作,说明该抽象可能过于粗糙。APE尝试寻找一个“更细粒度”的约简器来替换决策树中相应节点,从而将该模型动作分裂为多个不同的模型动作,增加区分度。 * 状态精化(State Refinement):这是最关键的优化。当发现一个状态s下的同一个模型动作π,导致了多个不同的目标状态(即存在非确定性转移,例如点击同一位置的按钮却跳转到不同页面),这表明抽象函数L无法准确区分导致不同行为的上下文。APE会尝试精化状态s中的某个模型动作的抽象,目标是使得这些产生不同结果的转移能被映射到不同的抽象动作或从不同的抽象状态出发,从而消除模型中的非确定性。 * 状态粗化(State Coarsening):为了防止持续的细粒度化导致状态爆炸,APE设置了一个回退机制。当一个新的、更精细的抽象函数将之前一个状态分裂成过多(超过预设阈值β)的新状态时,系统会认为此精化可能导致效率下降,从而回滚到之前的抽象函数,并将此次精化尝试加入黑名单。这确保了模型规模的可管理性。 4. 基于更新后的模型,采用一种结合随机和贪心的深度优先搜索策略,选择下一个要执行的模型动作π。选择时优先探索新发现的未访问动作,并考虑动作的访问频率和其对应的具体GUI动作数量。 5. 根据抽象函数L,将选定的模型动作π反向映射到当前GUI树中一组具体的GUI动作,并随机选择其中一个进行模拟执行(如点击),驱动应用进入新状态。 6. 重复上述循环,直至测试预算(如时间)耗尽。
通过这种基于运行时反馈(非确定性转移的出现)的动态精化与粗化机制,APE能够像使用反例引导的抽象精化(CEGAR, Counter-Example Guided Abstraction Refinement)一样,自动、逐步地找到一个在模型大小和精度之间取得良好平衡的抽象层次。
研究通过两个主要的评估来验证APE的有效性:
评估一:与前沿工具的对比实验(15个基准应用) * 研究对象:从Google Play商店选取了15个广泛使用的大型应用,包括Citymapper、Duolingo、Google Drive等,涵盖不同类型和复杂度。 * 对比工具:选取了三个最先进的测试工具作为基线:Monkey(随机测试)、Sapienz(基于搜索的测试)和STOAT(基于静态随机模型的测试)。 * 实验设置:每个工具在每个应用上运行5次,每次1小时(STOAT额外给予2小时基于模型的模糊测试时间)。在模拟器上进行,使用各自默认配置。 * 评估指标: * 覆盖率:活动(Activity)覆盖率、方法(Method)覆盖率和指令(Instruction)覆盖率。 * 缺陷检测能力:检测到的导致应用进程崩溃的唯一崩溃(unique crashes)数量,通过归一化的堆栈轨迹来识别唯一性。 * 模型特性:对比APE与STOAT所构建模型的状态数、动作数和转移数。
评估二:大规模实际应用评估(1,316个应用) * 研究对象:从Google Play商店选取1,316个流行应用。 * 实验设置:使用APE在真实的Nexus 5手机上对每个应用测试30分钟。 * 评估指标:检测到的崩溃总数、唯一崩溃数、涉及的错误类型,以及向开发者报告后获得确认和修复的情况。
ClassCastException等类型崩溃;STOAT的模型模糊测试有助于发现深层路径的崩溃;而APE则因其高覆盖率和精确模型,能触发一些需要复杂导航和特定上下文交互的崩溃。APE在1,316个真实应用的大规模测试中,从281个应用中总共发现了537个唯一崩溃,涉及42种错误类型(最常见的是NullPointerException)。研究人员向开发者报告了其中38个带有详细重现步骤的崩溃,截至论文发表时,已有13个被修复,5个被确认(待修复)。这强有力地证明了APE在工业场景中的实用性和有效性。
本研究的核心结论是:通过引入动态模型抽象与优化机制,APE能够自动演化出一个既精确又紧凑的GUI测试模型,从而在Android应用的自动化GUI测试中,显著超越现有最先进方法,实现更高的代码覆盖率和更强的缺陷检测能力。
其科学与应用价值体现在: 1. 方法论创新:首次将动态抽象精化(CEGAR思想)系统地应用于GUI测试模型的构建中,解决了静态抽象在精度与规模间的固有矛盾。 2. 技术创新:提出并使用决策树来表示和操纵抽象函数,为实现灵活、动态、可解释的抽象优化提供了有效手段。 3. 实用工具:实现了开源的、可直接替代Monkey的工业级工具APE。它无需修改被测应用或设备,兼容性强,已被工业伙伴采纳。 4. 实证贡献:通过大规模、严谨的对比实验和真实世界评估,为动态模型抽象方法的优越性提供了令人信服的证据,并为Android应用测试社区提供了高质量的基准数据和实用工具。