自动化测试实战:Android端验证码自动获取与填充方案
解决正式环境测试账号受限的优雅方案
背景与痛点
在移动应用自动化测试过程中,我们经常会遇到一个棘手的问题:正式环境的接口由于安全考虑,不愿意向测试团队开放测试账号。这意味着我们需要使用真实账号进行测试,而其中最麻烦的环节就是验证码的获取与填写。
传统的解决方案要么依赖mock数据(在正式环境不可行),要么手动查看手机短信并输入验证码(效率低下且无法自动化)。针对这一痛点,我开发了一个基于ADB的Android短信验证码自动获取工具,完美解决了正式环境下的验证码自动化测试问题。
技术方案概述
本方案的核心思路是通过ADB命令访问Android系统的短信内容提供器(Content Provider),获取短信内容后通过正则表达式提取验证码,最后自动填充到应用输入框中。
主要技术组件:
- ADB命令操作
- Android Content Provider访问
- 正则表达式匹配
- 自动化测试集成
核心代码实现
短信工具类 (SmsUtils)
python
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)
python
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系统的短信数据库,这是本方案的核心:
bash
adb shell content query --uri content://sms/inbox --projection _id,address,date,body
2. 短信时效性验证
考虑到验证码的有效期,我们添加了时间戳检查逻辑,确保只使用10分钟内的最新验证码。
3. 重试机制
通过装饰器实现了优雅的重试机制,应对短信延迟到达的情况,提高测试稳定性。
4. 关键字过滤
支持通过关键字过滤短信,确保获取到的是目标应用发送的验证码,避免其他短信干扰。
应用效果与价值
- 完全自动化:解决了正式环境验证码测试的最后一公里问题
- 提高效率:无需人工干预查看和输入验证码
- 增强稳定性:重试机制和时效验证确保测试可靠性
- 易于集成:模块化设计,可以轻松集成到现有测试框架中
注意事项与优化方向
- 权限要求:需要授予应用读取短信的权限(Android 6.0+需要动态申请)
- 兼容性:不同Android版本短信数据库结构可能略有差异
- 安全考虑:正式环境中使用需注意隐私保护问题
- 扩展性:可考虑支持多种验证码格式(4位、8位等)
未来可考虑扩展为支持多种验证方式(邮箱验证码、语音验证码等)的统一验证码获取平台。
结语
这个Android验证码自动获取方案已经在我们项目中稳定运行,大大提升了正式环境自动化测试的效率和可靠性。希望这个方案对大家有所启发,也欢迎交流更好的实现思路。