Android端验证码自动获取与填充的实战方案
作者:chunchun1221
在移动应用自动化测试过程中,我们经常会遇到一个棘手的问题:正式环境的接口由于安全考虑,不愿意向测试团队开放测试账号,传统的解决方案要么依赖mock数据要么手动查看手机短信并输入验证码,针对这一痛点,我开发了一个基于ADB的Android短信验证码自动获取工具
解决正式环境测试账号受限的优雅方案
背景与痛点
在移动应用自动化测试过程中,我们经常会遇到一个棘手的问题:正式环境的接口由于安全考虑,不愿意向测试团队开放测试账号。这意味着我们需要使用真实账号进行测试,而其中最麻烦的环节就是验证码的获取与填写。
传统的解决方案要么依赖mock数据(在正式环境不可行),要么手动查看手机短信并输入验证码(效率低下且无法自动化)。针对这一痛点,我开发了一个基于ADB的Android短信验证码自动获取工具,完美解决了正式环境下的验证码自动化测试问题。
技术方案概述
本方案的核心思路是通过ADB命令访问Android系统的短信内容提供器(Content Provider),获取短信内容后通过正则表达式提取验证码,最后自动填充到应用输入框中。
主要技术组件:
- ADB命令操作
- Android Content Provider访问
- 正则表达式匹配
- 自动化测试集成
核心代码实现
短信工具类 (SmsUtils)
import subprocess import time import re from utils.logger_until import Logger class SmsUtils: def __init__(self): self.logger = Logger(name=__name__).logger def get_sms_data_from_device(self): """从安卓设备读取所有短信""" command = "adb shell content query --uri content://sms/inbox --projection _id,address,date,body" result = subprocess.run(command, shell=True, capture_output=True, text=True, encoding="utf-8") if result.returncode != 0: raise Exception("设备获取短信失败,请检查ADB连接") # 解析短信数据 parsed_sms = [] for line in result.stdout.splitlines(): sms_dict = {} for item in line.split(","): key_value = item.split("=") if len(key_value) == 2: sms_dict[key_value[0].strip()] = key_value[1].strip() parsed_sms.append(sms_dict) return parsed_sms @staticmethod def filter_sms_by_keyword(sms_data, keyword): """根据关键字筛选短信""" return [sms for sms in sms_data if keyword in sms.get("body", "")] def get_latest_sms_by_timestamp(self, sms_data): """获取最新时间戳的短信""" sms_data_with_date = [sms for sms in sms_data if 'date' in sms] sorted_sms = sorted(sms_data_with_date, key=lambda x: int(x['date']), reverse=True) if not sorted_sms: return None # 验证短信时效性(10分钟内) latest_sms = sorted_sms[0] sms_timestamp = int(latest_sms['date']) current_timestamp = int(time.time() * 1000) time_diff = current_timestamp - sms_timestamp if time_diff <= 600000: # 10分钟 return latest_sms else: self.logger.error(f"验证码已过期,时间差:{time_diff//60000}分钟") return False def extract_verification_code(self, sms_body): """从短信内容提取验证码""" try: if isinstance(sms_body, dict): number_body = str(sms_body.get("body", "")) self.logger.info(f"原始短信内容: {number_body}") match = re.search(r"\d{6}", number_body) if match: return match.group(0) return None except Exception as e: self.logger.error(f"验证码提取错误: {e}") raise @staticmethod def get_latest_verification_code(keyword="魔意科技"): """获取最新验证码(整合方法)""" sms = SmsUtils() sms_data = sms.get_sms_data_from_device() filtered_sms = sms.filter_sms_by_keyword(sms_data, keyword) latest_sms = sms.get_latest_sms_by_timestamp(filtered_sms) if latest_sms: return sms.extract_verification_code(latest_sms) else: return False
登录页面集成 (LoginPage)
import time from functools import wraps from utils.sms_util import SmsUtils # 重试装饰器 def retry(times=3, delay=2): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): last_exception = None for attempt in range(times): try: return func(*args, **kwargs) except Exception as e: last_exception = e time.sleep(delay) print(f"第{attempt+1}次重试失败,剩余{times - attempt - 1}次重试") if last_exception: raise last_exception return wrapper return decorator class LoginPage(BasePage): def __init__(self, device_manager): super().__init__(device_manager) self.logger = Logger(name=__name__).logger @retry(times=3, delay=15) def auto_fill_verification_code(self, keyword="魔意科技"): """自动填充验证码(带重试机制)""" verification_code = SmsUtils.get_latest_verification_code(keyword) if verification_code: self.logger.info(f"验证码提取成功: {verification_code}") self.input_text("phone_code_input", verification_code) self.logger.info(f"验证码已自动填充") else: self.logger.error("未找到有效验证码") raise ValueError("验证码提取失败") def _perform_login_flow(self, account: str, locator_prefix: str): """执行登录流程""" # 确保在正确的登录方式页面 if not self.assert_element_visible(f"{locator_prefix}_input"): self.click_element(f"switch_to_{locator_prefix}") # 输入账号并获取验证码 self.input_text(f"{locator_prefix}_input", account) self.click_element("phone_code_get_captcha") # 等待并自动填充验证码 time.sleep(10) # 等待短信发送 self.auto_fill_verification_code() # 后续登录操作...
关键技术点解析
1. ADB访问短信内容提供器
通过adb shell content query
命令直接访问Android系统的短信数据库,这是本方案的核心:
adb shell content query --uri content://sms/inbox --projection _id,address,date,body
2. 短信时效性验证
考虑到验证码的有效期,我们添加了时间戳检查逻辑,确保只使用10分钟内的最新验证码。
3. 重试机制
通过装饰器实现了优雅的重试机制,应对短信延迟到达的情况,提高测试稳定性。
4. 关键字过滤
支持通过关键字过滤短信,确保获取到的是目标应用发送的验证码,避免其他短信干扰。
应用效果与价值
- 完全自动化:解决了正式环境验证码测试的最后一公里问题
- 提高效率:无需人工干预查看和输入验证码
- 增强稳定性:重试机制和时效验证确保测试可靠性
- 易于集成:模块化设计,可以轻松集成到现有测试框架中
注意事项与优化方向
- 权限要求:需要授予应用读取短信的权限(Android 6.0+需要动态申请)
- 兼容性:不同Android版本短信数据库结构可能略有差异
- 安全考虑:正式环境中使用需注意隐私保护问题
- 扩展性:可考虑支持多种验证码格式(4位、8位等)
未来可考虑扩展为支持多种验证方式(邮箱验证码、语音验证码等)的统一验证码获取平台。
结语
这个Android验证码自动获取方案已经在我们项目中稳定运行,大大提升了正式环境自动化测试的效率和可靠性。希望这个方案对大家有所启发,也欢迎交流更好的实现思路。
以上就是Android端验证码自动获取与填充的实战方案的详细内容,更多关于Android验证码自动获取与填充的资料请关注脚本之家其它相关文章!