杰瑞科技汇

Selenium Python如何判断元素存在?

Selenium 本身不提供断言方法,而是使用 Python 内置的 unittestpytest 等测试框架的断言功能,下面我将结合最常用的 pytest 框架,详细说明如何进行各种判断。


核心概念:等待

在进行任何判断之前,最重要的一步是等待,现代网页是动态加载的,元素不会瞬间出现,如果你不等待,代码可能会在元素还未加载时就尝试操作它,导致 NoSuchElementException 等错误。

Selenium 提供了两种等待方式:

  1. 隐式等待:全局设置,在整个会话期间都有效,它会告诉 WebDriver 在查找元素时,如果元素没有立即找到,就等待指定的秒数。

    driver.implicitly_wait(10)  # 最多等待10秒
    • 缺点:它只对 find_element 系列方法有效,并且不知道元素是否已经可交互(比如可见、可点击),它可能会在元素不可用时(如被遮挡)就返回元素,导致后续操作失败。
  2. 显式等待:针对特定条件进行等待,代码更健壮、更精确,这是强烈推荐的方式。

    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    # 等待最多10秒,直到元素可见
    element = WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located((By.ID, "myDynamicElement"))
    )
    • 优点:可以等待任意条件,如元素可见、可点击、存在、标题包含特定文本等,只有当条件满足时,代码才会继续执行,否则超时后抛出 TimeoutException

判断的几种主要场景

下面我们分场景介绍如何进行判断。

判断元素是否存在

这是最常见的判断,登录成功后,判断用户名是否出现。

方法:使用 try...except 结合 find_element

from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.by import By
def test_user_login(driver):
    # ... 登录操作 ...
    # 判断用户名元素是否存在
    try:
        username_element = driver.find_element(By.ID, "username-display")
        # 如果没抛出异常,说明元素存在
        assert username_element.is_displayed(), "用户名元素存在但未显示"
        print("登录成功,用户名已显示。")
    except NoSuchElementException:
        # 如果抛出异常,说明元素不存在
        assert False, "登录失败,未找到用户名元素。"

更优方法:结合显式等待

使用显式等待可以更优雅地处理元素不存在的情况,并设置超时时间。

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
def test_user_login_with_wait(driver):
    # ... 登录操作 ...
    try:
        # 等待最多10秒,直到用户名元素可见
        username_element = WebDriverWait(driver, 10).until(
            EC.visibility_of_element_located((By.ID, "username-display"))
        )
        print("登录成功,用户名已显示。")
    except TimeoutException:
        # 如果10秒后元素仍然不可见,则超时
        assert False, "登录失败,等待用户名元素超时。"

判断元素是否可见、可用

即使元素存在于 HTML 中,它也可能因为 display: none;opacity: 0; 而不可见,或者因为被其他元素遮挡而不可点击。

方法:使用 is_displayed()is_enabled()

def test_element_visibility(driver):
    driver.get("https://example.com/some-page")
    # 判断元素是否可见
    element = driver.find_element(By.ID, "submit-button")
    if element.is_displayed():
        print("提交按钮是可见的。")
    else:
        print("提交按钮是不可见的。")
    # 判断元素是否可用(未被禁用)
    if element.is_enabled():
        print("提交按钮是可点击的。")
        element.click()
    else:
        print("提交按钮被禁用了,无法点击。")

结合显式等待判断

def test_element_clickable_with_wait(driver):
    try:
        # 等待元素变为可点击状态(同时意味着它已可见且未被禁用)
        submit_button = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.ID, "submit-button"))
        )
        submit_button.click()
        print("成功点击了提交按钮。")
    except TimeoutException:
        assert False, "等待提交按钮变为可点击状态超时。"

判断文本内容

判断页面上的文本是否符合预期,例如验证成功消息、错误提示等。

方法:获取元素的 text 属性并进行比较

def test_success_message(driver):
    driver.get("https://example.com/after-login")
    # 方法1:直接获取并断言
    success_message = driver.find_element(By.CLASS_NAME, "success-msg").text
    assert "欢迎回来" in success_message, "成功消息内容不正确!"
    # 方法2:结合显式等待和断言
    try:
        message_element = WebDriverWait(driver, 10).until(
            EC.visibility_of_element_located((By.CLASS_NAME, "success-msg"))
        )
        # 使用 pytest 的断言,失败时会显示更清晰的错误信息
        assert "欢迎回来" in message_element.text, f"期望消息包含'欢迎回来',实际是: '{message_element.text}'"
    except TimeoutException:
        assert False, "等待成功消息显示超时。"

判断页面标题或 URL

判断页面是否跳转到了正确的页面。

方法:获取 titlecurrent_url

def test_page_navigation(driver):
    driver.get("https://example.com")
    login_link = driver.find_element(By.LINK_TEXT, "登录")
    login_link.click()
    # 判断页面标题
    assert "用户登录" in driver.title, "页面标题不正确!"
    # 判断页面URL
    assert "login" in driver.current_url, "页面URL不正确!"
    # 同样,这些判断也可以放在显式等待里
    WebDriverWait(driver, 10).until(
        EC.title_contains("用户登录")
    )
    print("成功导航到登录页面。")

判断元素属性

判断元素的某个属性值是否符合预期,hrefclassid 等。

方法:使用 get_attribute()

def test_link_attribute(driver):
    driver.get("https://example.com")
    logo_link = driver.find_element(By.CSS_SELECTOR, "a.logo")
    # 判断 href 属性
    expected_url = "https://example.com/home"
    actual_url = logo_link.get_attribute("href")
    assert actual_url == expected_url, f"Logo链接的URL不正确,期望: {expected_url}, 实际: {actual_url}"
    # 判断 class 属性
    assert "active" in logo_link.get_attribute("class"), "Logo链接未包含 'active' 类"

完整示例(使用 pytest

下面是一个完整的、结合了 pytest 和显式等待的测试用例示例。

test_login.py

import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
# 使用 fixture 来管理 WebDriver 实例
@pytest.fixture
def driver():
    # 初始化 WebDriver (这里以 Chrome 为例)
    d = webdriver.Chrome()
    d.implicitly_wait(5) # 可以设置一个较短的隐式等待作为保底
    yield d
    # 测试结束后关闭浏览器
    d.quit()
def test_successful_login(driver):
    """
    测试一个成功的登录流程
    """
    # 1. 打开登录页面
    driver.get("https://the-internet.herokuapp.com/login")
    # 2. 输入用户名和密码
    username_input = driver.find_element(By.ID, "username")
    password_input = driver.find_element(By.ID, "password")
    username_input.send_keys("tomsmith")
    password_input.send_keys("SuperSecretPassword!")
    # 3. 点击登录按钮
    login_button = driver.find_element(By.CSS_SELECTOR, "button[type='submit']")
    login_button.click()
    # 4. 判断:验证登录成功后的状态
    try:
        # 等待成功消息出现
        success_message = WebDriverWait(driver, 10).until(
            EC.visibility_of_element_located((By.ID, "flash"))
        )
        # 断言:消息中包含 "You logged into a secure area!"
        assert "You logged into a secure area!" in success_message.text
        # 额外断言:验证用户名是否显示在页面上
        logout_button = WebDriverWait(driver, 10).until(
            EC.visibility_of_element_located((By.XPATH, "//a/i[contains(text(), 'Logout')]"))
        )
        assert logout_button.is_displayed()
    except TimeoutException:
        # 如果等待超时,测试失败
        pytest.fail("登录后,预期元素未能在规定时间内出现。")
    print("测试用例 'test_successful_login' 通过!")

总结与最佳实践

  1. 优先使用显式等待:让你的测试脚本更稳定、更可靠,能更好地处理动态网页。
  2. 结合测试框架:使用 pytestunittestassert 语句,失败时会提供清晰的错误报告。
  3. 清晰的断言信息:在 assert 语句中提供详细的错误信息,方便快速定位问题。assert actual == expected, f"Expected {expected}, but got {actual}"
  4. 单一职责:一个测试用例最好只验证一个核心功能点,保持测试用例的简洁和专注。
  5. 处理异常:使用 try...except 来捕获 TimeoutException 等特定异常,并主动地让测试失败(例如使用 pytest.fail()),而不是让脚本意外终止。
分享:
扫描分享到社交APP
上一篇
下一篇