crmeb-app商城登录功能自动化测试代码编写总结
以为一个简简单单的登录功能,没想到做自动化测试会遇到这么多的困难,踩到这么多的坑。
坐标定位
属性完全无法定位的时候就采用坐标定位元素。
# 坐标点击方法
def base_tap_coordinate(self, x, y):
"""
基于坐标的点击方法
:param x: 点击点的横坐标
:param y: 点击点的纵坐标
"""
# 直接调用driver的tap方法
self.driver.tap([(x, y)])
# 可选:添加日志,便于调试
print(f"已通过坐标点击: ({x}, {y})")
time.sleep(2) # 等待页面响应
# 坐标输入方法
def base_input_coordinate(self, x, y, text):
"""
通过坐标实现输入:先点击坐标获得焦点,再输入文本
:param x: 输入框区域的横坐标
:param y: 输入框区域的纵坐标
:param text: 要输入的文本
"""
# 1. 点击输入框坐标,激活焦点
self.base_tap_coordinate(x, y)
# 2. 等待输入法或焦点稳定(根据App响应调整时间)
time.sleep(0.5)
# 3. 通过驱动发送文本(此方法会将文本输入到当前焦点元素)
self.driver.execute_script('mobile: type', {'text': text})
# 替代方案:使用ADB命令(更底层,但依赖外部环境)
# import os
# os.system(f'adb shell input text "{text}"')
安卓应用组件权限
java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.MAIN。。。。not exported from uid 10043
解决办法详见:https://www.cnblogs.com/malinyan/p/19458548
StaleElementReferenceException
这种错误一般都是偶发的,是一个经典的“竞态条件”问题。抓取到的元素引用,在正准备读取其文本的瞬间,这个元素被更新了,导致引用“过时”。
用一个简单的比喻来解释:
NoSuchElementError就像你想去图书馆借一本叫 《如何测试》 的书。你拿着索书号(定位器)在书库里找了10分钟(显式等待),但图书馆里根本就没有这本书(元素在页面中不存在)。最终你只能报告“找不到书”。
StaleElementReferenceException则是另一种情况。你去图书馆,成功找到了《如何测试》这本书,并把它从书架上拿在了手里(find_element成功,并获得了这个元素的“引用”或“指针”)。但是,在你走去登记台的路上,图书馆的管理系统突然更新,这本书被重新归类,原来的书架位置和书本信息全部刷新了(页面DOM/UI树更新)。这时你手里拿着的“书”的凭证(元素引用)已经失效,系统无法再根据这个旧的凭证处理这本书。当你试图登记(.text或.click())时,系统就会告诉你:“抱歉,您手里这本书的凭证已经过期了(Stale)”。
偶发原因:
因为这取决于数据加载速度与脚本执行速度的“竞赛”。
- 网络快,脚本慢:数据在脚本点击登录后立刻返回并渲染完成,脚本稍后才去获取昵称,此时页面早已稳定,成功。
- 网络慢,脚本快:数据还在路上,脚本已经找到了昵称元素(可能显示“加载中...”),紧接着数据到达,页面刷新,脚本再操作旧引用,抛出
Stale异常。
解决办法:
- **首选:优化等待时机 **
在点击后增加一个明确的等待,等待一个能代表“用户信息已100%加载完成”的稳定元素(如“退出登录”按钮)。这样能确保后续所有操作都在稳定状态下进行。
print("已点击登录按钮,等待登录成功...")
# --- 新增:等待登录成功且页面稳定的明确信号 ---
# 方案A: 等待“会员中心”按钮出现(假设它是登录后才有的)
try:
re = self.base_find_element(page.app_login_member)
print(f"登录成功,用户页面已稳定,标志性元素是{re.text}")
except TimeoutException:
# 方案B: 如果方案A不成立,改为等待昵称元素本身变得稳定(可交互)
print("未找到‘会员中心’,改为等待昵称元素稳定...")
self.base_find_element(page.app_login_nickname)
# --- 等待结束 ---
-
次选:使用支持重试的原子操作
当遇到
Stale异常时,会自动重新执行“查找+获取”的完整流程,从而获得全新的、有效的元素引用。这相当于“如果凭证过期,我就立即去图书馆按最新信息再找一次这本书”。def base_get_text_with_retry(self, loc, max_retries=5): """ 获取元素文本,遇到StaleElementReferenceException时自动重试。 :param loc: 定位器 :param max_retries: 最大重试次数 :return: 元素的文本内容 """ attempt = 0 while attempt < max_retries: try: element = self.base_find_element(loc) return element.text except StaleElementReferenceException: attempt += 1 print(f"警告: 获取文本时元素过期,正在进行第 {attempt} 次重试...") time.sleep(0.5) # 重试前稍等片刻 raise Exception(f"在重试 {max_retries} 次后,仍无法从定位器 {loc} 获取稳定文本。")

浙公网安备 33010602011771号