1. 问题本质与核心原因剖析“selenium.common.exceptions.JavascriptException: Message: javascript error: $ is not defined” 这个错误乍一看是JavaScript执行报错但根源往往不在你的测试脚本逻辑本身而在于Selenium与浏览器页面交互的时机和状态。简单来说就是你的Selenium脚本试图在一个还没有加载jQuery或其他定义了$符号的库的页面上执行了依赖于$的JavaScript代码。$是jQuery库的经典别名。在Web开发中为了简化DOM操作开发者通常会引入jQuery库之后就可以用$(‘#id’)这样的语法来选取元素。然而一个页面从开始加载到完全就绪是分阶段的。Selenium的driver.get(url)命令只是告诉浏览器“去导航到这个地址”它并不会等待页面所有资源特别是像jQuery这样的外部JavaScript库都下载并执行完毕。如果你的脚本紧接着就执行driver.execute_script(“$(‘#submit’).click()”)而此时浏览器还在加载jQuery的.js文件或者jQuery代码尚未执行那么全局作用域里自然就没有$这个函数于是“$ is not defined”的错误就抛给了Selenium。更深一层看这个问题是Web自动化测试中的一个典型“竞态条件”问题。你的自动化脚本执行速度与网络延迟、浏览器渲染、JavaScript解释执行的速度在赛跑。脚本跑赢了错误就发生了。因此解决这个问题的核心思路从“如何修复一句JavaScript代码”转变为“如何让我的自动化脚本智能地等待页面达到我所期望的可用状态”。2. 解决方案全景从临时修复到根治策略遇到这个问题不同经验的测试开发者可能会采取不同层级的解决方案。我们可以将其分为“应急补丁”、“标准等待”和“架构防御”三个层面。2.1 应急补丁直接注入jQuery或使用原生JS当你只是想快速让某个脚本跑起来或者你确定页面最终会加载jQuery只是脚本执行得太快时可以采取一些临时性措施。方法一在执行脚本前动态注入jQuery如果目标页面本身没有使用jQuery但你的测试脚本又重度依赖它可以考虑先注入jQuery库。不过这可能会污染测试环境影响对页面本身行为的验证需谨慎使用。from selenium import webdriver driver webdriver.Chrome() driver.get(“your_url”) # 检查jQuery是否已加载 jquery_loaded driver.execute_script(“return typeof jQuery ! ‘undefined’;“) if not jquery_loaded: # 从CDN加载jQuery driver.execute_script(“”“ var script document.createElement(‘script’); script.src ‘https://code.jquery.com/jquery-3.6.0.min.js’; document.head.appendChild(script); ”“”) # 需要等待jQuery加载完成 WebDriverWait(driver, 10).until( lambda d: d.execute_script(“return typeof jQuery ! ‘undefined’;”) ) # 现在可以安全使用$ driver.execute_script(“$(‘button’).click();”)方法二放弃$使用原生JavaScript或Selenium原生API这是最干净、最推荐的做法。既然Selenium提供了强大的原生定位能力就应优先使用。用document.querySelector替代$# 原jQuery写法$(‘#submit’) # 替代原生JS写法 driver.execute_script(“document.querySelector(‘#submit’).click();”) # 或者更复杂的示例原jQuery: $(‘.btn.active’).text() text driver.execute_script(“return document.querySelector(‘.btn.active’).textContent;“)直接用Selenium的find_element和click() 这通常是更好的选择因为Selenium内置了等待和重试机制。from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC submit_button WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, “submit”)) ) submit_button.click()注意EC.presence_of_element_located只要求元素存在于DOM中而EC.element_to_be_clickable还要求元素可见、可交互对于点击操作后者更安全。2.2 标准等待使用显式等待Explicit Wait这是解决此类问题的标准答案和最佳实践。显式等待让你可以定义条件Selenium会轮询检查直到条件满足或超时。这完美解决了页面加载时机不确定的问题。等待jQuery加载完成from selenium.webdriver.support.ui import WebDriverWait def wait_for_jquery(driver, timeout30): “”“等待页面jQuery加载并处于就绪状态。”“” try: # 条件1: jQuery对象已定义 WebDriverWait(driver, timeout).until( lambda d: d.execute_script(“return typeof jQuery ! ‘undefined’;”) ) # 条件2: jQuery文档就绪状态为true可选但更严谨 WebDriverWait(driver, timeout).until( lambda d: d.execute_script(“return jQuery.active 0;”) ) print(“jQuery已加载就绪。”) except TimeoutException: print(“等待jQuery加载超时。页面可能未使用jQuery或加载失败。”) # 这里可以记录日志或抛出更具体的异常 # 在页面跳转或可能触发重新加载的操作后调用 driver.get(“https://example.com”) wait_for_jquery(driver) # 现在执行依赖$的脚本 driver.execute_script(“$(‘#element’).hide();”)等待特定元素出现更通用对于任何操作最稳健的方式是等待目标元素达到可交互状态。from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC # 等待一个ID为‘dynamic-content’的元素出现 element WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, “dynamic-content”)) ) # 等待一个按钮可点击 clickable_button WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, “.btn-primary”)) ) clickable_button.click()2.3 架构防御页面对象模式Page Object Model与智能等待封装在大型或长期的自动化项目中将等待逻辑封装起来是提升代码可维护性和健壮性的关键。页面对象模式POM是标准做法。1. 创建基础页面类BasePage封装通用等待方法# base_page.py from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class BasePage: def __init__(self, driver): self.driver driver self.timeout 10 def wait_for_jquery(self, timeoutNone): “”“等待jQuery就绪。”“” wait_time timeout or self.timeout try: WebDriverWait(self.driver, wait_time).until( lambda d: d.execute_script(“return typeof jQuery ! ‘undefined’ jQuery.active 0;”) ) except TimeoutException: # 不是所有页面都需要jQuery这里可以记录debug日志而非直接报错 pass def find_element(self, by, locator, timeoutNone): “”“查找单个元素自动加入显式等待。”“” wait_time timeout or self.timeout return WebDriverWait(self.driver, wait_time).until( EC.presence_of_element_located((by, locator)) ) def find_clickable_element(self, by, locator, timeoutNone): “”“查找可点击元素。”“” wait_time timeout or self.timeout return WebDriverWait(self.driver, wait_time).until( EC.element_to_be_clickable((by, locator)) ) def execute_script_safe(self, script, *args): “”“安全执行JS脚本执行前等待jQuery如果需要的话。”“” # 简单判断脚本是否包含$符号不严谨但可作为示例 if ‘$’ in script: self.wait_for_jquery() return self.driver.execute_script(script, *args)2. 具体的页面类继承BasePage定义页面元素和操作# login_page.py from selenium.webdriver.common.by import By from base_page import BasePage class LoginPage(BasePage): # 定位器 USERNAME_INPUT (By.ID, “username”) PASSWORD_INPUT (By.ID, “password”) LOGIN_BUTTON (By.CSS_SELECTOR, “button[type‘submit’]”) ERROR_MESSAGE (By.CLASS_NAME, “alert-error”) def login(self, username, password): “”“登录操作所有步骤都内置了等待。”“” self.find_element(*self.USERNAME_INPUT).send_keys(username) self.find_element(*self.PASSWORD_INPUT).send_keys(password) self.find_clickable_element(*self.LOGIN_BUTTON).click() def get_error_message(self): “”“获取错误信息等待错误信息出现。”“” try: element WebDriverWait(self.driver, 5).until( EC.visibility_of_element_located(self.ERROR_MESSAGE) ) return element.text except TimeoutException: return None # 在测试用例中使用 def test_login_failure(): driver webdriver.Chrome() driver.get(“login_page_url”) login_page LoginPage(driver) login_page.login(“wrong_user”, “wrong_pass”) assert “Invalid credentials” in login_page.get_error_message()通过这种架构$ is not defined这类错误被基础设施层消化了。测试用例编写者只需关注业务逻辑无需反复编写等待代码。3. 深入排查当标准等待失效时有时候即使使用了显式等待问题依然出现。这时候就需要进行更深入的排查。3.1 检查页面是否使用了$的其他含义极少数情况下页面可能使用了其他库如Prototype.js也占用了$符号或者开发者用var $ document.querySelector做了别名。这时你的jQuery代码会与原生实现冲突。可以在浏览器控制台输入$并回车查看其定义。在Selenium中也可以what_is_$ driver.execute_script(“return $ ? $.fn.jquery ? ‘jQuery v’ $.fn.jquery : ‘$ is defined but not jQuery’ : ‘$ is undefined’;“) print(what_is_$)3.2 检查iframe或Shadow DOM如果你的操作对象位于iframe或 Shadow DOM 内部那么你需要先切换上下文。在iframe中$可能只存在于该iframe的文档作用域内。# 切换到iframe iframe driver.find_element(By.TAG_NAME, “iframe”) driver.switch_to.frame(iframe) # 现在在iframe内部执行操作 wait_for_jquery(driver) # 等待iframe内的jQuery driver.execute_script(“$(‘#innerElement’).click();”) # 操作完成后切回主文档 driver.switch_to.default_content()3.3 检查浏览器扩展或注入脚本某些浏览器扩展或公司安全软件会向页面注入脚本可能意外地修改或移除了全局变量。尝试在无痕模式或禁用所有扩展的浏览器配置下运行测试。from selenium import webdriver from selenium.webdriver.chrome.options import Options options Options() options.add_argument(“—incognito”) # 无痕模式 options.add_argument(“—disable-extensions”) # 禁用扩展 driver webdriver.Chrome(optionsoptions)3.4 升级或匹配驱动版本正如网络搜索片段中用户遇到的问题从ChromeDriver 89升级到91后出现错误浏览器、驱动和Selenium版本不匹配是经典问题。确保你的ChromeDriver版本与已安装的Chrome浏览器主版本号一致。使用chrome://version/查看浏览器版本然后去官方站点下载对应的驱动。4. 实操心得与避坑指南在我多年的Selenium自动化实践中围绕“$ is not defined”以及更广义的“元素未找到”、“脚本执行错误”等问题积累了一些血泪教训。4.1 永远不要依赖隐式等待Implicit Wait作为主要等待策略隐式等待driver.implicitly_wait(10)设置了一个全局的查找元素超时时间。它有两个大问题1) 它只对find_element系列方法生效对execute_script无效2) 它会和显式等待混合导致总等待时间不可预测。我的建议是要么完全不用隐式等待要么将其设置为一个很小的值如2-3秒作为基础容错核心等待逻辑一律使用显式等待。4.2presence_of_element_located与visibility_of_element_located的抉择presence_of_element_located: 元素存在于DOM树中即可哪怕它不可见display: none或visibility: hidden。适用于你需要获取元素属性如>from selenium.webdriver.support.ui import WebDriverWait def text_to_be_present_in_element(locator, text): “”“自定义等待条件元素包含特定文本。”“” def _predicate(driver): try: element_text driver.find_element(*locator).text return text in element_text except StaleElementReferenceException: # 元素可能已刷新返回False让等待继续 return False return _predicate # 使用 wait WebDriverWait(driver, 10) locator (By.ID, “status-message”) wait.until(text_to_be_present_in_element(locator, “加载完成”))4.4 关于execute_async_script的使用如果你的页面有大量基于Promise或回调的异步操作execute_script可能仍然会在异步操作完成前返回。这时可以考虑使用execute_async_script。它的最后一个参数callback是一个由Selenium注入的函数你需要在你的异步JavaScript代码中调用它来表示脚本执行完毕。# 示例等待页面自定义的异步初始化完成 script “”“ var callback arguments[arguments.length - 1]; // 假设页面有一个标志全局状态的Promise window.myAppInitializationPromise.then(function() { callback(‘初始化完成’); }).catch(function(err) { callback(‘初始化失败: ‘ err); }); ”“” result driver.execute_async_script(script) print(result)4.5 日志与截图是排查的利器在等待前后加入日志和截图能在CI/CD流水线中快速定位问题。import logging from datetime import datetime logging.basicConfig(levellogging.INFO) logger logging.getLogger(__name__) def click_with_logging(driver, by, locator, timeout10): element_desc f“{by}{locator}” logger.info(f“尝试点击元素: {element_desc}”) try: element WebDriverWait(driver, timeout).until( EC.element_to_be_clickable((by, locator)) ) element.click() logger.info(f“成功点击: {element_desc}”) except TimeoutException: logger.error(f“等待元素可点击超时: {element_desc}”) # 失败时截图 timestamp datetime.now().strftime(“%Y%m%d_%H%M%S”) driver.save_screenshot(f“timeout_{timestamp}.png”) raise处理“$ is not defined”错误远不止于在代码前加一句wait。它触及了Web自动化测试的核心挑战——处理动态、异步的现代Web应用。从理解错误本质开始优先采用原生Selenium API或纯JavaScript方案将显式等待作为标准配置最终在架构层面通过页面对象模式和智能封装来构建健壮的测试框架这才是彻底告别此类错误的专业路径。每一次遇到这样的错误都是一个提醒你去审视和加固测试代码稳定性的机会。
Selenium自动化测试中解决‘$ is not defined‘错误的完整指南
1. 问题本质与核心原因剖析“selenium.common.exceptions.JavascriptException: Message: javascript error: $ is not defined” 这个错误乍一看是JavaScript执行报错但根源往往不在你的测试脚本逻辑本身而在于Selenium与浏览器页面交互的时机和状态。简单来说就是你的Selenium脚本试图在一个还没有加载jQuery或其他定义了$符号的库的页面上执行了依赖于$的JavaScript代码。$是jQuery库的经典别名。在Web开发中为了简化DOM操作开发者通常会引入jQuery库之后就可以用$(‘#id’)这样的语法来选取元素。然而一个页面从开始加载到完全就绪是分阶段的。Selenium的driver.get(url)命令只是告诉浏览器“去导航到这个地址”它并不会等待页面所有资源特别是像jQuery这样的外部JavaScript库都下载并执行完毕。如果你的脚本紧接着就执行driver.execute_script(“$(‘#submit’).click()”)而此时浏览器还在加载jQuery的.js文件或者jQuery代码尚未执行那么全局作用域里自然就没有$这个函数于是“$ is not defined”的错误就抛给了Selenium。更深一层看这个问题是Web自动化测试中的一个典型“竞态条件”问题。你的自动化脚本执行速度与网络延迟、浏览器渲染、JavaScript解释执行的速度在赛跑。脚本跑赢了错误就发生了。因此解决这个问题的核心思路从“如何修复一句JavaScript代码”转变为“如何让我的自动化脚本智能地等待页面达到我所期望的可用状态”。2. 解决方案全景从临时修复到根治策略遇到这个问题不同经验的测试开发者可能会采取不同层级的解决方案。我们可以将其分为“应急补丁”、“标准等待”和“架构防御”三个层面。2.1 应急补丁直接注入jQuery或使用原生JS当你只是想快速让某个脚本跑起来或者你确定页面最终会加载jQuery只是脚本执行得太快时可以采取一些临时性措施。方法一在执行脚本前动态注入jQuery如果目标页面本身没有使用jQuery但你的测试脚本又重度依赖它可以考虑先注入jQuery库。不过这可能会污染测试环境影响对页面本身行为的验证需谨慎使用。from selenium import webdriver driver webdriver.Chrome() driver.get(“your_url”) # 检查jQuery是否已加载 jquery_loaded driver.execute_script(“return typeof jQuery ! ‘undefined’;“) if not jquery_loaded: # 从CDN加载jQuery driver.execute_script(“”“ var script document.createElement(‘script’); script.src ‘https://code.jquery.com/jquery-3.6.0.min.js’; document.head.appendChild(script); ”“”) # 需要等待jQuery加载完成 WebDriverWait(driver, 10).until( lambda d: d.execute_script(“return typeof jQuery ! ‘undefined’;”) ) # 现在可以安全使用$ driver.execute_script(“$(‘button’).click();”)方法二放弃$使用原生JavaScript或Selenium原生API这是最干净、最推荐的做法。既然Selenium提供了强大的原生定位能力就应优先使用。用document.querySelector替代$# 原jQuery写法$(‘#submit’) # 替代原生JS写法 driver.execute_script(“document.querySelector(‘#submit’).click();”) # 或者更复杂的示例原jQuery: $(‘.btn.active’).text() text driver.execute_script(“return document.querySelector(‘.btn.active’).textContent;“)直接用Selenium的find_element和click() 这通常是更好的选择因为Selenium内置了等待和重试机制。from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC submit_button WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, “submit”)) ) submit_button.click()注意EC.presence_of_element_located只要求元素存在于DOM中而EC.element_to_be_clickable还要求元素可见、可交互对于点击操作后者更安全。2.2 标准等待使用显式等待Explicit Wait这是解决此类问题的标准答案和最佳实践。显式等待让你可以定义条件Selenium会轮询检查直到条件满足或超时。这完美解决了页面加载时机不确定的问题。等待jQuery加载完成from selenium.webdriver.support.ui import WebDriverWait def wait_for_jquery(driver, timeout30): “”“等待页面jQuery加载并处于就绪状态。”“” try: # 条件1: jQuery对象已定义 WebDriverWait(driver, timeout).until( lambda d: d.execute_script(“return typeof jQuery ! ‘undefined’;”) ) # 条件2: jQuery文档就绪状态为true可选但更严谨 WebDriverWait(driver, timeout).until( lambda d: d.execute_script(“return jQuery.active 0;”) ) print(“jQuery已加载就绪。”) except TimeoutException: print(“等待jQuery加载超时。页面可能未使用jQuery或加载失败。”) # 这里可以记录日志或抛出更具体的异常 # 在页面跳转或可能触发重新加载的操作后调用 driver.get(“https://example.com”) wait_for_jquery(driver) # 现在执行依赖$的脚本 driver.execute_script(“$(‘#element’).hide();”)等待特定元素出现更通用对于任何操作最稳健的方式是等待目标元素达到可交互状态。from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC # 等待一个ID为‘dynamic-content’的元素出现 element WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, “dynamic-content”)) ) # 等待一个按钮可点击 clickable_button WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, “.btn-primary”)) ) clickable_button.click()2.3 架构防御页面对象模式Page Object Model与智能等待封装在大型或长期的自动化项目中将等待逻辑封装起来是提升代码可维护性和健壮性的关键。页面对象模式POM是标准做法。1. 创建基础页面类BasePage封装通用等待方法# base_page.py from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class BasePage: def __init__(self, driver): self.driver driver self.timeout 10 def wait_for_jquery(self, timeoutNone): “”“等待jQuery就绪。”“” wait_time timeout or self.timeout try: WebDriverWait(self.driver, wait_time).until( lambda d: d.execute_script(“return typeof jQuery ! ‘undefined’ jQuery.active 0;”) ) except TimeoutException: # 不是所有页面都需要jQuery这里可以记录debug日志而非直接报错 pass def find_element(self, by, locator, timeoutNone): “”“查找单个元素自动加入显式等待。”“” wait_time timeout or self.timeout return WebDriverWait(self.driver, wait_time).until( EC.presence_of_element_located((by, locator)) ) def find_clickable_element(self, by, locator, timeoutNone): “”“查找可点击元素。”“” wait_time timeout or self.timeout return WebDriverWait(self.driver, wait_time).until( EC.element_to_be_clickable((by, locator)) ) def execute_script_safe(self, script, *args): “”“安全执行JS脚本执行前等待jQuery如果需要的话。”“” # 简单判断脚本是否包含$符号不严谨但可作为示例 if ‘$’ in script: self.wait_for_jquery() return self.driver.execute_script(script, *args)2. 具体的页面类继承BasePage定义页面元素和操作# login_page.py from selenium.webdriver.common.by import By from base_page import BasePage class LoginPage(BasePage): # 定位器 USERNAME_INPUT (By.ID, “username”) PASSWORD_INPUT (By.ID, “password”) LOGIN_BUTTON (By.CSS_SELECTOR, “button[type‘submit’]”) ERROR_MESSAGE (By.CLASS_NAME, “alert-error”) def login(self, username, password): “”“登录操作所有步骤都内置了等待。”“” self.find_element(*self.USERNAME_INPUT).send_keys(username) self.find_element(*self.PASSWORD_INPUT).send_keys(password) self.find_clickable_element(*self.LOGIN_BUTTON).click() def get_error_message(self): “”“获取错误信息等待错误信息出现。”“” try: element WebDriverWait(self.driver, 5).until( EC.visibility_of_element_located(self.ERROR_MESSAGE) ) return element.text except TimeoutException: return None # 在测试用例中使用 def test_login_failure(): driver webdriver.Chrome() driver.get(“login_page_url”) login_page LoginPage(driver) login_page.login(“wrong_user”, “wrong_pass”) assert “Invalid credentials” in login_page.get_error_message()通过这种架构$ is not defined这类错误被基础设施层消化了。测试用例编写者只需关注业务逻辑无需反复编写等待代码。3. 深入排查当标准等待失效时有时候即使使用了显式等待问题依然出现。这时候就需要进行更深入的排查。3.1 检查页面是否使用了$的其他含义极少数情况下页面可能使用了其他库如Prototype.js也占用了$符号或者开发者用var $ document.querySelector做了别名。这时你的jQuery代码会与原生实现冲突。可以在浏览器控制台输入$并回车查看其定义。在Selenium中也可以what_is_$ driver.execute_script(“return $ ? $.fn.jquery ? ‘jQuery v’ $.fn.jquery : ‘$ is defined but not jQuery’ : ‘$ is undefined’;“) print(what_is_$)3.2 检查iframe或Shadow DOM如果你的操作对象位于iframe或 Shadow DOM 内部那么你需要先切换上下文。在iframe中$可能只存在于该iframe的文档作用域内。# 切换到iframe iframe driver.find_element(By.TAG_NAME, “iframe”) driver.switch_to.frame(iframe) # 现在在iframe内部执行操作 wait_for_jquery(driver) # 等待iframe内的jQuery driver.execute_script(“$(‘#innerElement’).click();”) # 操作完成后切回主文档 driver.switch_to.default_content()3.3 检查浏览器扩展或注入脚本某些浏览器扩展或公司安全软件会向页面注入脚本可能意外地修改或移除了全局变量。尝试在无痕模式或禁用所有扩展的浏览器配置下运行测试。from selenium import webdriver from selenium.webdriver.chrome.options import Options options Options() options.add_argument(“—incognito”) # 无痕模式 options.add_argument(“—disable-extensions”) # 禁用扩展 driver webdriver.Chrome(optionsoptions)3.4 升级或匹配驱动版本正如网络搜索片段中用户遇到的问题从ChromeDriver 89升级到91后出现错误浏览器、驱动和Selenium版本不匹配是经典问题。确保你的ChromeDriver版本与已安装的Chrome浏览器主版本号一致。使用chrome://version/查看浏览器版本然后去官方站点下载对应的驱动。4. 实操心得与避坑指南在我多年的Selenium自动化实践中围绕“$ is not defined”以及更广义的“元素未找到”、“脚本执行错误”等问题积累了一些血泪教训。4.1 永远不要依赖隐式等待Implicit Wait作为主要等待策略隐式等待driver.implicitly_wait(10)设置了一个全局的查找元素超时时间。它有两个大问题1) 它只对find_element系列方法生效对execute_script无效2) 它会和显式等待混合导致总等待时间不可预测。我的建议是要么完全不用隐式等待要么将其设置为一个很小的值如2-3秒作为基础容错核心等待逻辑一律使用显式等待。4.2presence_of_element_located与visibility_of_element_located的抉择presence_of_element_located: 元素存在于DOM树中即可哪怕它不可见display: none或visibility: hidden。适用于你需要获取元素属性如>from selenium.webdriver.support.ui import WebDriverWait def text_to_be_present_in_element(locator, text): “”“自定义等待条件元素包含特定文本。”“” def _predicate(driver): try: element_text driver.find_element(*locator).text return text in element_text except StaleElementReferenceException: # 元素可能已刷新返回False让等待继续 return False return _predicate # 使用 wait WebDriverWait(driver, 10) locator (By.ID, “status-message”) wait.until(text_to_be_present_in_element(locator, “加载完成”))4.4 关于execute_async_script的使用如果你的页面有大量基于Promise或回调的异步操作execute_script可能仍然会在异步操作完成前返回。这时可以考虑使用execute_async_script。它的最后一个参数callback是一个由Selenium注入的函数你需要在你的异步JavaScript代码中调用它来表示脚本执行完毕。# 示例等待页面自定义的异步初始化完成 script “”“ var callback arguments[arguments.length - 1]; // 假设页面有一个标志全局状态的Promise window.myAppInitializationPromise.then(function() { callback(‘初始化完成’); }).catch(function(err) { callback(‘初始化失败: ‘ err); }); ”“” result driver.execute_async_script(script) print(result)4.5 日志与截图是排查的利器在等待前后加入日志和截图能在CI/CD流水线中快速定位问题。import logging from datetime import datetime logging.basicConfig(levellogging.INFO) logger logging.getLogger(__name__) def click_with_logging(driver, by, locator, timeout10): element_desc f“{by}{locator}” logger.info(f“尝试点击元素: {element_desc}”) try: element WebDriverWait(driver, timeout).until( EC.element_to_be_clickable((by, locator)) ) element.click() logger.info(f“成功点击: {element_desc}”) except TimeoutException: logger.error(f“等待元素可点击超时: {element_desc}”) # 失败时截图 timestamp datetime.now().strftime(“%Y%m%d_%H%M%S”) driver.save_screenshot(f“timeout_{timestamp}.png”) raise处理“$ is not defined”错误远不止于在代码前加一句wait。它触及了Web自动化测试的核心挑战——处理动态、异步的现代Web应用。从理解错误本质开始优先采用原生Selenium API或纯JavaScript方案将显式等待作为标准配置最终在架构层面通过页面对象模式和智能封装来构建健壮的测试框架这才是彻底告别此类错误的专业路径。每一次遇到这样的错误都是一个提醒你去审视和加固测试代码稳定性的机会。