You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Selenium使用PageFactory编写POM测试报错解决方案

Selenium PageFactory 登录脚本报错排查与修复

问题根因汇总

你遇到的两个报错(递归深度溢出、super对象无__getattr__属性)都不是递归逻辑写法问题,也不是单纯元素加载慢导致的,核心是代码存在4个显性错误:

  • 核心根因:子类构造方法未调用父类初始化方法。SignInPage继承PageFactory后,重写的__init__方法仅赋值了self.driver,没有执行super().__init__(driver),父类构造方法中初始化的mobile_test、timeout等实例属性完全未创建。当PageFactory内置的__getattr__方法运行时,首先访问self.mobile_test,但该属性不存在就会再次触发__getattr__,形成无限递归,这就是RecursionError的来源,手动上调递归深度限制无法解决逻辑层面的死循环问题。
  • 导入与类名、定位器键名不匹配:测试文件中导入的是SignUpPage类,实际实例化时调用的是SignInPage,属于基础语法错误;同时堆栈日志显示代码中调用了self.user_name.set_text(),但locators字典中根本没有定义user_name这个定位键,访问不存在的定位器时,__getattr__方法走完locator匹配逻辑后会调用父类的__getattr__,但Python原生object类没有该方法,就会抛出AttributeError: 'super' object has no attribute '__getattr__'。
  • 类名定位器写法错误:Selenium的CLASS_NAME定位策略仅支持传入单个类名,你给登录按钮传入的class值包含空格(多个类名拼接),永远无法定位到元素。
  • 输入操作多余的换行符:set_text()方法本身会向输入框传入指定文本,你额外加\n可能触发非预期的提前表单提交逻辑。

修复后的代码

1. 页面对象文件(建议将sign_up_page.py重命名为sign_in_page.py,和类名保持一致)

from seleniumpagefactory.Pagefactory import PageFactory
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

class SignInPage(PageFactory):
    def __init__(self, driver):
        # 必须调用父类构造方法,可自定义显式等待超时时间
        super().__init__(driver, timeout=15)
        self.driver = driver

    # 定位器写法修正:复合类名改用CSS选择器
    locators = {
        "mail": ("ID", "userEmail"),
        "pwd": ("ID", "password"),
        "login_btn": ("CSS", "button.customStyle__ButtonDefault___1k2JQ.btn-primary")
    }

    def enter_username(self, username):
        # 等待元素可点击后再输入,替代硬等待
        WebDriverWait(self.driver, 10).until(
            EC.element_to_be_clickable((By.ID, "userEmail"))
        )
        self.mail.set_text(username)

    def enter_password(self, password):
        WebDriverWait(self.driver, 10).until(
            EC.element_to_be_clickable((By.ID, "password"))
        )
        self.pwd.set_text(password)
    
    def click_login_btn(self):
        WebDriverWait(self.driver, 10).until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, "button.customStyle__ButtonDefault___1k2JQ.btn-primary"))
        )
        self.login_btn.click_button()

2. 测试执行文件test_book.py

import sys
import unittest
from selenium import webdriver
# 注意导入路径和类名匹配
from src.pages.sign_in_page import SignInPage

class TestLogin(unittest.TestCase):
    def testLog(self):
        print(sys.getrecursionlimit())
        driver = webdriver.Chrome()
        # 短时长隐式等待作为全局兜底
        driver.implicitly_wait(5)
        driver.get("https://pubpp-developers.myriadgroup.mobi/login")
        # 删除无意义的time.sleep(10),用显式等待替代
        user = SignInPage(driver)
        user.enter_username('address@gmail.com')
        user.enter_password('abcdef123')
        user.click_login_btn()

        # 测试完成后回收浏览器资源
        driver.quit()

if __name__ == "__main__":
    unittest.main()

页面元素未加载完成的标准处理方案

  • 禁止滥用time.sleep()硬等待:这种写法不管元素有没有加载完都会等固定时长,既拖慢测试执行效率,也会因为网络波动、页面渲染慢等场景出现等待时长不足的问题,稳定性极差。
  • 优先用显式等待:针对每个操作的元素,明确指定等待条件(输入框、按钮等可交互元素用EC.element_to_be_clickable,仅需判断元素存在用EC.presence_of_element_located,判断元素可见用EC.visibility_of_element_located),等待到条件满足立刻执行后续操作,超时没等到就抛出异常,稳定性和执行效率最优。
  • 合理配置PageFactory内置超时:selenium-page-factory默认的元素等待超时是10秒,可以在调用父类构造方法时通过timeout参数自定义全局等待时长,作为显式等待的兜底。
  • 全局隐式等待作为补充:可以在driver初始化后设置一个短时长的隐式等待(3-5秒即可),定位元素时会自动轮询查找,但注意隐式等待无法判断元素是否可点击、可见,不能替代显式等待。
  • 定位器优先用稳定属性:少用层级复杂的绝对XPATH、动态变化的随机class属性,优先用固定ID、name属性、自定义测试属性(比如data-testid)定位元素,减少定位失败概率。

内容的提问来源于stack exchange,提问作者Aminata Willane

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 16:36:28