最近在使用Appium做App自动化测试的时候,遇到需要读取短信验证码的场景。在网上查阅了很多资料,大致有以下几种方案:
- 通过下拉手机状态栏从消息中心获取短信验证码,或者打开短信App读取验证码
- 请开发帮助在测试环境写一个万能验证码
- 从数据库或者Redis缓存中读取已存储的验证码
- 自己写一个专用App读取手机的短信验证码并存储,然后每次从已存储的文件中读取验证码 (很佩服这种方式和能力)
方案1最让我担心的是通过UI的方式可能导致不稳定;方案2和3看起来要更容易些,但是在和开发讨论之后,发现2不是一个好的实践,3在我们的项目中不可行;方案4很棒,但是需要具备App开发的能力,作为一个只能勉强写写测试代码的QA,表示做不到啊T_T。
无奈之下本打算使用方案1,但却无意间在appium pro网站上发现了一个获取Android手机状态栏通知消息的官方实现(Retrieving Status Bar Notifications for Android Devices), 感觉整个世界都变美好了,哈哈。尝试之后发现这个方案很稳定,也很方便,只是需要做一些额外的配置。在这里对整个过程做一下记录,方便自己以后再次使用。
方案简单介绍
Appium Team通过Appium Settings Helper App实现了一种读取通知的方法。 Appium Settings Helper App 是一个在Appium会话启动时自动安装在手机上的小型应用程序,在在第一次连接手机进行测试时会自动安装。主要是用来弥补一些通过UiAutomator2或Espresso不可实现的设备级功能。
Appium Settings应用程序可以被授予阅读任何传入通知的权限。然后,它可以使用ADB保存这些通知,以便由Appium主进程检索。这个复杂的过程被封装在了一个Appium命令中(在Appium 1.16中首次发布):
driver.execute_script("mobile: getNotifications")
完整的实现分为以下四步:
1. 配置Appium Settings应用权限
下面的步骤仅针对三星手机,其他手机需自行百度。
设置 -> 应用程序 点击右上角的三个点 -> 特殊访问 -> 通知访问 打开Appium Settings的开关即可
2. 读取消息中心的消息
driver.execute_script("mobile: getNotifications")
读取的结果是一个字典,每一条消息都是一个键值对,且最新的消息在最前面。
3. 通过正则表达式提取验证码
将上一步的结果转换为字符串,然后使用正则表达式去匹配短信模版获取验证码。由于不能保证每次测试前消息中心都处于清空状态,所以可以获取所有的验证码,然后取其中的第一个。
class SmsCode(object):
def __init__(self, driver):
self.driver = driver
self.codes = []
# 获取所有验证码
def _get_all_codes(self):
notifications = str(self.driver.execute_script("mobile: getNotifications"))
pattern = re.compile(r'【某网站用户身份验证】您的验证码(\d{6}),该验证码5分钟内有效,请勿泄漏于他人!')
self.codes = pattern.findall(notifications)
return self.codes
# 获取最新验证码
def _get_code(self):
return self.codes[0] if len(self.codes) else None
4. 循环等待新的验证码
在点击了发送验证码按钮之后,使用循环等待的方式去获取最新的验证码。
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!')