Android端驗(yàn)證碼自動(dòng)獲取與填充的實(shí)戰(zhàn)方案
解決正式環(huán)境測(cè)試賬號(hào)受限的優(yōu)雅方案
背景與痛點(diǎn)
在移動(dòng)應(yīng)用自動(dòng)化測(cè)試過程中,我們經(jīng)常會(huì)遇到一個(gè)棘手的問題:正式環(huán)境的接口由于安全考慮,不愿意向測(cè)試團(tuán)隊(duì)開放測(cè)試賬號(hào)。這意味著我們需要使用真實(shí)賬號(hào)進(jìn)行測(cè)試,而其中最麻煩的環(huán)節(jié)就是驗(yàn)證碼的獲取與填寫。
傳統(tǒng)的解決方案要么依賴mock數(shù)據(jù)(在正式環(huán)境不可行),要么手動(dòng)查看手機(jī)短信并輸入驗(yàn)證碼(效率低下且無法自動(dòng)化)。針對(duì)這一痛點(diǎn),我開發(fā)了一個(gè)基于ADB的Android短信驗(yàn)證碼自動(dòng)獲取工具,完美解決了正式環(huán)境下的驗(yàn)證碼自動(dòng)化測(cè)試問題。
技術(shù)方案概述
本方案的核心思路是通過ADB命令訪問Android系統(tǒng)的短信內(nèi)容提供器(Content Provider),獲取短信內(nèi)容后通過正則表達(dá)式提取驗(yàn)證碼,最后自動(dòng)填充到應(yīng)用輸入框中。
主要技術(shù)組件:
- ADB命令操作
- Android Content Provider訪問
- 正則表達(dá)式匹配
- 自動(dòng)化測(cè)試集成
核心代碼實(shí)現(xiàn)
短信工具類 (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):
"""從安卓設(shè)備讀取所有短信"""
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("設(shè)備獲取短信失敗,請(qǐng)檢查ADB連接")
# 解析短信數(shù)據(jù)
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):
"""根據(jù)關(guān)鍵字篩選短信"""
return [sms for sms in sms_data if keyword in sms.get("body", "")]
def get_latest_sms_by_timestamp(self, sms_data):
"""獲取最新時(shí)間戳的短信"""
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
# 驗(yàn)證短信時(shí)效性(10分鐘內(nèi))
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"驗(yàn)證碼已過期,時(shí)間差:{time_diff//60000}分鐘")
return False
def extract_verification_code(self, sms_body):
"""從短信內(nèi)容提取驗(yàn)證碼"""
try:
if isinstance(sms_body, dict):
number_body = str(sms_body.get("body", ""))
self.logger.info(f"原始短信內(nèi)容: {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"驗(yàn)證碼提取錯(cuò)誤: {e}")
raise
@staticmethod
def get_latest_verification_code(keyword="魔意科技"):
"""獲取最新驗(yàn)證碼(整合方法)"""
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="魔意科技"):
"""自動(dòng)填充驗(yàn)證碼(帶重試機(jī)制)"""
verification_code = SmsUtils.get_latest_verification_code(keyword)
if verification_code:
self.logger.info(f"驗(yàn)證碼提取成功: {verification_code}")
self.input_text("phone_code_input", verification_code)
self.logger.info(f"驗(yàn)證碼已自動(dòng)填充")
else:
self.logger.error("未找到有效驗(yàn)證碼")
raise ValueError("驗(yàn)證碼提取失敗")
def _perform_login_flow(self, account: str, locator_prefix: str):
"""執(zhí)行登錄流程"""
# 確保在正確的登錄方式頁面
if not self.assert_element_visible(f"{locator_prefix}_input"):
self.click_element(f"switch_to_{locator_prefix}")
# 輸入賬號(hào)并獲取驗(yàn)證碼
self.input_text(f"{locator_prefix}_input", account)
self.click_element("phone_code_get_captcha")
# 等待并自動(dòng)填充驗(yàn)證碼
time.sleep(10) # 等待短信發(fā)送
self.auto_fill_verification_code()
# 后續(xù)登錄操作...
關(guān)鍵技術(shù)點(diǎn)解析
1. ADB訪問短信內(nèi)容提供器
通過adb shell content query命令直接訪問Android系統(tǒng)的短信數(shù)據(jù)庫,這是本方案的核心:
adb shell content query --uri content://sms/inbox --projection _id,address,date,body
2. 短信時(shí)效性驗(yàn)證
考慮到驗(yàn)證碼的有效期,我們添加了時(shí)間戳檢查邏輯,確保只使用10分鐘內(nèi)的最新驗(yàn)證碼。
3. 重試機(jī)制
通過裝飾器實(shí)現(xiàn)了優(yōu)雅的重試機(jī)制,應(yīng)對(duì)短信延遲到達(dá)的情況,提高測(cè)試穩(wěn)定性。
4. 關(guān)鍵字過濾
支持通過關(guān)鍵字過濾短信,確保獲取到的是目標(biāo)應(yīng)用發(fā)送的驗(yàn)證碼,避免其他短信干擾。
應(yīng)用效果與價(jià)值
- 完全自動(dòng)化:解決了正式環(huán)境驗(yàn)證碼測(cè)試的最后一公里問題
- 提高效率:無需人工干預(yù)查看和輸入驗(yàn)證碼
- 增強(qiáng)穩(wěn)定性:重試機(jī)制和時(shí)效驗(yàn)證確保測(cè)試可靠性
- 易于集成:模塊化設(shè)計(jì),可以輕松集成到現(xiàn)有測(cè)試框架中
注意事項(xiàng)與優(yōu)化方向
- 權(quán)限要求:需要授予應(yīng)用讀取短信的權(quán)限(Android 6.0+需要?jiǎng)討B(tài)申請(qǐng))
- 兼容性:不同Android版本短信數(shù)據(jù)庫結(jié)構(gòu)可能略有差異
- 安全考慮:正式環(huán)境中使用需注意隱私保護(hù)問題
- 擴(kuò)展性:可考慮支持多種驗(yàn)證碼格式(4位、8位等)
未來可考慮擴(kuò)展為支持多種驗(yàn)證方式(郵箱驗(yàn)證碼、語音驗(yàn)證碼等)的統(tǒng)一驗(yàn)證碼獲取平臺(tái)。
結(jié)語
這個(gè)Android驗(yàn)證碼自動(dòng)獲取方案已經(jīng)在我們項(xiàng)目中穩(wěn)定運(yùn)行,大大提升了正式環(huán)境自動(dòng)化測(cè)試的效率和可靠性。希望這個(gè)方案對(duì)大家有所啟發(fā),也歡迎交流更好的實(shí)現(xiàn)思路。
以上就是Android端驗(yàn)證碼自動(dòng)獲取與填充的實(shí)戰(zhàn)方案的詳細(xì)內(nèi)容,更多關(guān)于Android驗(yàn)證碼自動(dòng)獲取與填充的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android DrawerLayout實(shí)現(xiàn)側(cè)拉菜單功能
這篇文章主要介紹了Android DrawerLayout實(shí)現(xiàn)側(cè)拉菜單功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-06-06
Android Activity的跳轉(zhuǎn)與傳值詳解
這篇文章主要介紹了Android Activity的跳轉(zhuǎn)與傳值詳解的相關(guān)資料,需要的朋友可以參考下2017-06-06
android操作SQLite增刪改減實(shí)現(xiàn)代碼
android操作SQLite增刪改減實(shí)現(xiàn)代碼,學(xué)習(xí)android的朋友可以參考下。2010-11-11
Android實(shí)現(xiàn)png轉(zhuǎn)jpg圖片的方法
在 Android 應(yīng)用開發(fā)中,圖像處理是非常常見的需求,本文介紹了如何在 Android平臺(tái)上實(shí)現(xiàn)一個(gè) PNG 轉(zhuǎn) JPG 的模塊,用戶可以從相冊(cè)或文件中選取 PNG 圖片,一鍵將其轉(zhuǎn)換為 JPG 并保存到本地,需要的朋友可以參考下2025-04-04
Android編程讀取Assets所有文件(遍歷每一個(gè)文件夾)并存入sdcard的方法
這篇文章主要介紹了Android編程讀取Assets所有文件(遍歷每一個(gè)文件夾)并存入sdcard的方法,涉及Android針對(duì)文件與目錄的遍歷及I/O操作相關(guān)技巧,需要的朋友可以參考下2016-02-02
Android上傳文件到Web服務(wù)器 PHP接收文件
這篇文章主要為大家詳細(xì)介紹了Android上傳文件到Web服務(wù)器,PHP接收文件的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
老項(xiàng)目遷移AndroidStudio3.0遇到的坑
給大家分享了老的項(xiàng)目以及程序遷移到了AndroidStudio3.0遇到的坑和問題解決辦法,希望給你做個(gè)參考。2017-11-11
Android內(nèi)存泄漏排查利器LeakCanary
這篇文章主要為大家詳細(xì)介紹了Android內(nèi)存泄漏排查利器LeakCanary的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03

