最近在使用Appium做App自動(dòng)化測(cè)試的時(shí)候,遇到需要讀取短信驗(yàn)證碼的場(chǎng)景。在網(wǎng)上查閱了很多資料,大致有以下幾種方案:
- 通過(guò)下拉手機(jī)狀態(tài)欄從消息中心獲取短信驗(yàn)證碼,或者打開短信App讀取驗(yàn)證碼
- 請(qǐng)開發(fā)幫助在測(cè)試環(huán)境寫一個(gè)萬(wàn)能驗(yàn)證碼
- 從數(shù)據(jù)庫(kù)或者Redis緩存中讀取已存儲(chǔ)的驗(yàn)證碼
- 自己寫一個(gè)專用App讀取手機(jī)的短信驗(yàn)證碼并存儲(chǔ),然后每次從已存儲(chǔ)的文件中讀取驗(yàn)證碼 (很佩服這種方式和能力)
方案1最讓我擔(dān)心的是通過(guò)UI的方式可能導(dǎo)致不穩(wěn)定;方案2和3看起來(lái)要更容易些,但是在和開發(fā)討論之后,發(fā)現(xiàn)2不是一個(gè)好的實(shí)踐,3在我們的項(xiàng)目中不可行;方案4很棒,但是需要具備App開發(fā)的能力,作為一個(gè)只能勉強(qiáng)寫寫測(cè)試代碼的QA,表示做不到啊T_T。
無(wú)奈之下本打算使用方案1,但卻無(wú)意間在appium pro網(wǎng)站上發(fā)現(xiàn)了一個(gè)獲取Android手機(jī)狀態(tài)欄通知消息的官方實(shí)現(xiàn)(Retrieving Status Bar Notifications for Android Devices), 感覺整個(gè)世界都變美好了,哈哈。嘗試之后發(fā)現(xiàn)這個(gè)方案很穩(wěn)定,也很方便,只是需要做一些額外的配置。在這里對(duì)整個(gè)過(guò)程做一下記錄,方便自己以后再次使用。
方案簡(jiǎn)單介紹
Appium Team通過(guò)Appium Settings Helper App實(shí)現(xiàn)了一種讀取通知的方法。 Appium Settings Helper App 是一個(gè)在Appium會(huì)話啟動(dòng)時(shí)自動(dòng)安裝在手機(jī)上的小型應(yīng)用程序,在在第一次連接手機(jī)進(jìn)行測(cè)試時(shí)會(huì)自動(dòng)安裝。主要是用來(lái)彌補(bǔ)一些通過(guò)UiAutomator2或Espresso不可實(shí)現(xiàn)的設(shè)備級(jí)功能。
Appium Settings應(yīng)用程序可以被授予閱讀任何傳入通知的權(quán)限。然后,它可以使用ADB保存這些通知,以便由Appium主進(jìn)程檢索。這個(gè)復(fù)雜的過(guò)程被封裝在了一個(gè)Appium命令中(在Appium 1.16中首次發(fā)布):
driver.execute_script("mobile: getNotifications")
完整的實(shí)現(xiàn)分為以下四步:
1. 配置Appium Settings應(yīng)用權(quán)限
下面的步驟僅針對(duì)三星手機(jī),其他手機(jī)需自行百度。
設(shè)置 -> 應(yīng)用程序 點(diǎn)擊右上角的三個(gè)點(diǎn) -> 特殊訪問 -> 通知訪問 打開Appium Settings的開關(guān)即可
2. 讀取消息中心的消息
driver.execute_script("mobile: getNotifications")
讀取的結(jié)果是一個(gè)字典,每一條消息都是一個(gè)鍵值對(duì),且最新的消息在最前面。
3. 通過(guò)正則表達(dá)式提取驗(yàn)證碼
將上一步的結(jié)果轉(zhuǎn)換為字符串,然后使用正則表達(dá)式去匹配短信模版獲取驗(yàn)證碼。由于不能保證每次測(cè)試前消息中心都處于清空狀態(tài),所以可以獲取所有的驗(yàn)證碼,然后取其中的第一個(gè)。
class SmsCode(object):
def __init__(self, driver):
self.driver = driver
self.codes = []
# 獲取所有驗(yàn)證碼
def _get_all_codes(self):
notifications = str(self.driver.execute_script("mobile: getNotifications"))
pattern = re.compile(r'【某網(wǎng)站用戶身份驗(yàn)證】您的驗(yàn)證碼(\d{6}),該驗(yàn)證碼5分鐘內(nèi)有效,請(qǐng)勿泄漏于他人!')
self.codes = pattern.findall(notifications)
return self.codes
# 獲取最新驗(yàn)證碼
def _get_code(self):
return self.codes[0] if len(self.codes) else None
4. 循環(huán)等待新的驗(yàn)證碼
在點(diǎn)擊了發(fā)送驗(yàn)證碼按鈕之后,使用循環(huán)等待的方式去獲取最新的驗(yàn)證碼。
def wait_for_new_code(self):
old_codes = self._get_all_codes()
print(old_codes)
for i in range(30):
time.sleep(3)
new_codes = self._get_all_codes()
print(new_codes)
if len(new_codes) > len(old_codes):
return self._get_code()
pytest.fail('SMS code not received within 90 seconds!')