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异常

解决办法:

  1. **首选:优化等待时机 **

​ 在点击后增加一个明确的等待,等待一个能代表“用户信息已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)
        # --- 等待结束 ---
  1. 次选:使用支持重试的原子操作

    当遇到 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} 获取稳定文本。")
    
posted @ 2026-01-11 14:37  蜀道,难  阅读(2)  评论(0)    收藏  举报