Selenium 本身不提供断言方法,而是使用 Python 内置的 unittest 或 pytest 等测试框架的断言功能,下面我将结合最常用的 pytest 框架,详细说明如何进行各种判断。
核心概念:等待
在进行任何判断之前,最重要的一步是等待,现代网页是动态加载的,元素不会瞬间出现,如果你不等待,代码可能会在元素还未加载时就尝试操作它,导致 NoSuchElementException 等错误。
Selenium 提供了两种等待方式:
-
隐式等待:全局设置,在整个会话期间都有效,它会告诉 WebDriver 在查找元素时,如果元素没有立即找到,就等待指定的秒数。
driver.implicitly_wait(10) # 最多等待10秒
- 缺点:它只对
find_element系列方法有效,并且不知道元素是否已经可交互(比如可见、可点击),它可能会在元素不可用时(如被遮挡)就返回元素,导致后续操作失败。
- 缺点:它只对
-
显式等待:针对特定条件进行等待,代码更健壮、更精确,这是强烈推荐的方式。
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
判断页面是否跳转到了正确的页面。
方法:获取 title 或 current_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("成功导航到登录页面。")
判断元素属性
判断元素的某个属性值是否符合预期,href、class、id 等。
方法:使用 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' 通过!")
总结与最佳实践
- 优先使用显式等待:让你的测试脚本更稳定、更可靠,能更好地处理动态网页。
- 结合测试框架:使用
pytest或unittest的assert语句,失败时会提供清晰的错误报告。 - 清晰的断言信息:在
assert语句中提供详细的错误信息,方便快速定位问题。assert actual == expected, f"Expected {expected}, but got {actual}"。 - 单一职责:一个测试用例最好只验证一个核心功能点,保持测试用例的简洁和专注。
- 处理异常:使用
try...except来捕获TimeoutException等特定异常,并主动地让测试失败(例如使用pytest.fail()),而不是让脚本意外终止。
