2. 权限请求UI基本原理
移动端权限请求UI是自动化测试中绕不开的关卡。Android与iOS两套权限模型在弹窗类型、触发机制、生命周期上存在本质差异,系统级应用与第三方应用的权限获取路径也截然不同。只有彻底理解这些底层差异,才能写出稳定、跨平台的UI自动化用例。本章从权限弹窗类型、系统vs第三方权限区别、生命周期三个维度拆解原理,为后续脚本编写和定位策略打下基础。
2.1 移动端权限模型概述
权限模型决定了应用何时以何种方式向用户展示弹窗。Android采用运行时权限模型,在API 23之后将敏感权限的授权时机从安装时推迟到运行时;iOS则采用权限服务模型,所有敏感权限必须在首次访问时通过系统弹窗获得用户明确同意。两大模型的核心理念对比如下:
| 对比维度 | Android | iOS |
|---|---|---|
| 授权时机 | 代码主动调用 requestPermissions | 系统自动弹窗(首次使用时) |
| 弹窗控制权 | 开发者可自定义触发时机 | 系统严格控制弹窗出现时机 |
| 权限分级 | Normal / Dangerous / Signature / Privileged | 所有敏感权限统一管理 |
| 用户撤回方式 | 设置 → 应用 → 权限(可单项关闭) | 设置 → 隐私 → 对应权限(可单项关闭) |
| 厂商/系统定制 | ROM厂商可深度定制弹窗UI | 弹窗样式高度统一 |
自动化测试启示:Android弹窗的UI差异(不同ROM)要求测试脚本必须具备弹窗元素定位的容错机制;iOS弹窗统一度高,定位策略相对稳定,但需关注iOS版本迭代对弹窗文案的微调。
2.2 Android权限弹窗类型
Android的权限弹窗根据权限分组和授权选项可分为以下几类:
- 标准运行时弹窗:包含“允许”和“拒绝”两个按钮,适用于大多数危险权限。
- 仅在使用中允许弹窗:Android 11新增,提供“仅在使用中允许”“每次询问”“拒绝”三选项,针对位置、麦克风、相机等持续敏感权限。
- 一次性权限弹窗:Android 11引入,授权后应用仅在当前生命周期内可用,进程重启后需重新申请。
- 权限分组弹窗:当应用同时申请同一分组的多个权限时,系统可能合并展示。
以下是一个典型的Android运行时权限请求代码,触发系统弹窗:
// Kotlin — 请求相机权限
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.CAMERA),
REQUEST_CODE_CAMERA
)
// 回调处理
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
if (requestCode == REQUEST_CODE_CAMERA) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 授权成功 — UI切换到可用状态
} else {
// 授权拒绝 — 展示引导提示
}
}
}
厂商定制陷阱:小米、OPPO、华为等ROM会替换系统弹窗为自有样式,按钮文案、布局、甚至出现“始终允许”的额外选项。自动化测试必须为不同ROM维护独立的弹窗元素定位库。
2.3 iOS权限弹窗类型
iOS的权限弹窗由系统统一管理,根据权限服务类型展示不同的授权选项。主要弹窗类型包括:
| 权限服务 | 授权选项 | 弹窗触发方式 |
|---|---|---|
| 位置 | 仅一次 / 使用期间 / 始终 | CLLocationManager requestWhenInUseAuthorization |
| 相机 | 允许 / 不允许 | AVCaptureDevice requestAccess(for: .video) |
| 麦克风 | 允许 / 不允许 | AVAudioSession requestRecordPermission |
| 相册 | 允许全部 / 允许选中的照片 / 不允许 | PHPhotoLibrary requestAuthorization |
| 通知 | 允许 / 不允许 | UNUserNotificationCenter requestAuthorization |
iOS应用必须在Info.plist中声明权限用途描述,否则弹窗不会出现:
<key>NSCameraUsageDescription</key>
<string>需要使用相机扫描二维码</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>需要获取位置信息用于导航</string>
测试要点:iOS弹窗文案完全由Info.plist中的描述字段决定。自动化测试可通过修改描述字段来验证不同文案下的弹窗布局是否正常。
2.4 系统级与第三方应用权限区别
系统级应用(如系统设置、预装电话应用)与第三方应用在权限获取上存在显著差异,这些差异直接影响自动化测试的覆盖范围:
| 对比维度 | 系统级应用 | 第三方应用 |
|---|---|---|
| 权限等级 | 可申请 Signature / Privileged 等级 | 仅可申请 Normal / Dangerous 等级 |
| 预置权限 | 预置权限白名单,部分权限免弹窗 | 无任何预置权限 |
| 弹窗豁免 | 系统签名权限直接授权,不弹窗 | 所有危险权限必须弹窗 |
| 审核机制 | 系统签名验证 + 平台签名证书 | 应用商店审核 + 运行时弹窗 |
| 自动化测试重点 | 通常无需UI弹窗验证 | 必须覆盖全量弹窗类型 |
核心结论:第三方应用的权限弹窗UI自动化测试是重中之重。系统级应用的权限测试更多依赖adb shell命令或私有API,而非UI交互。
2.5 权限生命周期
权限从未申请到最终状态经历一系列状态转换,每个状态对应不同的UI表现和测试关注点:
| 生命周期阶段 | 状态 | UI表现 | 自动化测试关注点 |
|---|---|---|---|
| 阶段一 | 未授权(初始) | 功能按钮可用,无弹窗 | 确认初始权限状态为false |
| 阶段二 | 弹窗展示(申请中) | 系统弹窗覆盖当前页面 | 弹窗元素定位 & 等待策略 |
| 阶段三 | 已授权 | 功能正常使用,弹窗消失 | 验证权限回调成功,UI切换到可用态 |
| 阶段四 | 已拒绝 | 功能受限,可能展示引导条 | 验证拒绝后UI提示文案 & 引导按钮 |
| 阶段五 | 撤回(设置中关闭) | 回到未授权状态,触发二次弹窗 | 验证二次弹窗行为与首次是否一致 |
以下流程图直观展示了权限从申请到最终状态的核心流转路径,以及Android/iOS弹窗类型在流程中的位置:
2.6 权限弹窗UI自动化测试关注点
基于上述原理,权限弹窗UI自动化测试需重点覆盖以下维度:
- 弹窗出现时机验证:确认弹窗在首次调用权限API时准确出现,非首次调用时不再重复弹窗。
- 弹窗元素定位:针对Android不同ROM和iOS系统版本维护独立的定位策略(resource-id / accessibility-id / 文案匹配)。
- 授权/拒绝状态断言:点击“允许”后验证权限回调为granted;点击“拒绝”后验证功能UI进入受限态。
- 生命周期覆盖:从首次申请 → 授权/拒绝 → 设置撤回 → 二次弹窗,形成闭环验证。
- 异常场景:弹窗被系统拦截、权限设置页面跳转、多次快速点击授权按钮等。
章节小结:Android与iOS的权限弹窗在类型、触发方式、生命周期上各有特点。系统级应用与第三方应用的权限差异决定了自动化测试的覆盖范围。理解这些底层原理,后续的环境搭建和脚本编写才能有的放矢。