还在手动写 Selenium 测试?SeleniumBase 让你的自动化效率提升 10 倍
为什么 SeleniumBase 值得关注
Selenium 是自动化测试领域最受欢迎的框架之一,但用过它的人都知道,原生 Selenium 存在诸多痛点:等待时间难以把控、截图和日志收集繁琐、测试报告不直观、并行执行配置复杂。这些问题会消耗开发者大量时间,甚至让人怀疑自动化测试是否真的值得。
SeleniumBase 正是为解决这些问题而生。这是一个基于 Python 的测试框架,在 Selenium WebDriver 基础上封装了大量实用功能,让你可以把注意力集中在测试逻辑本身,而不是被各种配置和兼容性细节所困扰。
SeleniumBase 的核心优势包括:内置智能等待机制省去手动设置 implicitly_wait 的麻烦、自动截图和录屏让问题排查一目了然、集成 pytest生态可以无缝使用各种插件、内置 Dashboard 模式提供美观的测试报告、支持浏览器自动化和 API 模式、一行命令即可实现测试并行化。这些特性使得 SeleniumBase 成为从个人项目到企业级应用的理想选择。
无论你是刚接触 Web 自动化测试的新手,还是希望提升现有测试效率的老兵,SeleniumBase 都值得一试。它不会让你抛弃已有的 Selenium 知识,而是以你熟悉的方式工作,同时大幅降低出错概率和维护成本。
环境搭建:从零开始配置 SeleniumBase
系统要求
在开始之前,确保你的系统满足以下要求:Python 3.7 或更高版本、 pip 包管理器、主流浏览器(Chrome、Firefox、Edge 等)以及对应的 WebDriver。SeleniumBase 支持 Linux、macOS 和 Windows 三大平台。
安装步骤
安装 SeleniumBase 非常简单,打开终端执行以下命令即可:
pip install seleniumbase
如果你需要使用额外的功能,比如 PDF 处理或视觉比较,可以安装完整版本:
pip install seleniumbase[all]
安装完成后,验证环境是否正确配置:
python -c "import seleniumbase; print(seleniumbase.__version__)"
浏览器驱动管理
SeleniumBase 内置了浏览器驱动管理工具,可以自动下载和配置所需的 WebDriver。你只需要运行:
seleniumbase install chromium
这条命令会自动下载 ChromeDriver 并配置好环境变量。对于 Firefox 或 Edge,运行对应的命令即可。支持的浏览器包括 chromium、firefox、edge、safari 和 ie。
创建第一个测试文件
建议创建一个专门的测试目录来存放测试代码:
mkdir selenium_tests
cd selenium_tests
然后创建一个简单的测试文件来验证环境是否正常工作。
核心功能详解
1. 简化的测试语法
SeleniumBase 的核心设计理念是让测试代码更简洁。相比原生 Selenium,它提供了更高层次的抽象。你不再需要记住各种复杂的 WebDriver API,一个简单的例子就能看出区别。
假设你要打开一个网页并点击按钮。用原生 Selenium 写法需要这样:
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
driver = webdriver.Chrome()
driver.get("https://example.com")
wait = WebDriverWait(driver, 10)
button = wait.until(EC.element_to_be_clickable((By.ID, "my-button")))
button.click()
driver.quit()
用 SeleniumBase 的写法变成了:
from seleniumbase import BaseCase
class MyTests(BaseCase):
def test_example(self):
self.open("https://example.com")
self.click("#my-button")
代码量大幅减少,可读性显著提升。所有常见的 WebDriver 操作都被封装成了简洁的方法,如 open、click、type、get_text 等。
2. 智能等待机制
在 Web 自动化测试中,等待是最容易出问题的环节。元素还没加载完成就尝试操作会导致测试失败,而盲目添加固定延时又会拖慢整个测试套件的执行速度。
SeleniumBase 内置了智能等待机制,会自动检测元素何时可用。你不再需要手动设置 implicitly_wait 或编写复杂的显式等待代码。框架会持续尝试直到元素可交互或超时,这大大提高了测试的稳定性和执行效率。
对于特殊场景,你仍然可以使用自定义等待:
self.wait_for_element_visible("#id", timeout=20)
self.wait_for_text("期望的文本内容", "#id")
self.wait_for_element_not_present(".loading-spinner")
3. 丰富的断言方法
SeleniumBase 提供了大量实用的断言方法,让测试验证更加直观:
self.assert_element_present("#header")
self.assert_text("欢迎登录", "#title")
self.assert_attribute("#input", "placeholder", "请输入用户名")
self.assert_url("https://example.com/dashboard")
self.assert_title("仪表盘")
这些方法会在断言失败时自动截取当前页面,帮助你快速定位问题。
4. 自动截图和录屏
当测试失败时,问题排查往往是最耗时的环节。SeleniumBase 会在测试失败时自动截取屏幕截图,并保存完整的控制台日志。你可以在测试报告中直接查看失败时的页面状态。
要手动截图也非常简单:
self.save_screenshot("step1.png")
如果需要录制整个测试过程,启用视频录制功能:
self.enable_recording()
self.open("https://example.com")
self.do_something()
self.disable_recording()
5. 浏览器配置灵活性
SeleniumBase 允许你灵活配置浏览器启动参数,比如无头模式、禁用图片加载、设置代理等:
self.open("https://example.com", window_size=(1920, 1080))
你也可以在启动时指定浏览器选项:
@classmethod
def setUpClass(cls):
cls.driver = None
cls.options = webdriver.ChromeOptions()
cls.options.add_argument("--disable-extensions")
6. Dashboard 模式
SeleniumBase 的一大亮点是内置的 Dashboard 功能。运行测试后,它会生成一个交互式的 HTML 报告,展示测试结果、失败原因、截图等信息:
pytest test_file.py --dashboard
生成的报告保存在 htmlcov 目录下,可以用浏览器直接打开查看。报告中包含了测试通过率、耗时统计、失败详情等丰富信息。
7. API 模式和可视化测试
除了传统的浏览器自动化,SeleniumBase 还支持 API 测试和视觉比较。你可以轻松验证 REST 接口的响应,也可以对页面截图进行像素级对比,确保 UI 没有意外变化。
实战教程:一步步构建完整测试
第一步:基础测试结构
创建一个 Python 文件,导入 SeleniumBase 的基类,然后编写你的测试类:
from seleniumbase import BaseCase
class LoginTests(BaseCase):
"""登录功能测试套件"""
def setUp(self):
"""每个测试方法执行前的准备工作"""
super().setUp()
# 访问登录页面
self.open("https://practice.automationtesting.in/login/")
def tearDown(self):
"""每个测试方法执行后的清理工作"""
# 可以在此处添加自定义清理逻辑
super().tearDown()
def test_successful_login(self):
"""测试正确的用户名和密码可以成功登录"""
# 输入用户名
self.type("#username", "testuser@example.com")
# 输入密码
self.type("#password", "TestPassword123!")
# 点击登录按钮
self.click("[name='login']")
# 验证登录成功,跳转到账户页面
self.wait_for_url_contains("account")
self.assert_element_present(".woocommerce-MyAccount-content")
这个例子展示了 SeleniumBase 测试的基本结构。每个测试类继承自 BaseCase,就自动获得了大量实用方法。setUp 和 tearDown 方法分别在每个测试前后执行,用于初始化和清理工作。
第二步:处理复杂交互
实际项目中,表单交互往往比简单的输入和点击更复杂。来看几个常见场景的处理方法。
拖拽操作:
def test_drag_and_drop(self):
"""测试拖拽功能"""
self.open("https://jqueryui.com/droppable/")
# 切换到 iframe
self.switch_to_frame(".demo-frame")
# 执行拖拽操作
self.drag_and_drop("#draggable", "#droppable")
# 验证放置成功
self.assert_text("Dropped!", "#droppable")
# 切换回主文档
self.switch_to_default_content()
文件上传:
def test_file_upload(self):
"""测试文件上传功能"""
self.open("https://practice.automationtesting.in/upload/")
# 使用绝对路径上传文件
self.choose_file("#upload_file", "/path/to/test-image.png")
# 点击上传按钮
self.click("#submit_button")
# 验证上传成功
self.wait_for_text("uploaded successfully", timeout=10)
处理下拉菜单:
def test_dropdown_selection(self):
"""测试下拉菜单选择"""
self.open("https://practice.automationtesting.in/dropdown/")
# 选择下拉选项
self.select_option_by_text("#抽筋dropdown", "Option 3")
# 验证选择成功
selected_value = self.get_selected_option("#抽筋dropdown")
self.assert_true("Option 3" in selected_value)
处理弹窗和确认对话框:
def test_javascript_dialogs(self):
"""测试 JavaScript 弹窗处理"""
self.open("https://practice.automationtesting.in/javascript-alert/")
# 点击触发确认弹窗的按钮
self.click("button.confirm")
# 接受弹窗
self.accept_alert()
# 或者拒绝弹窗
self.click("button.confirm")
self.dismiss_alert()
第三步:数据驱动测试
SeleniumBase 支持数据驱动测试,可以用不同的数据多次运行同一个测试用例。这对于测试登录功能、搜索功能等场景特别有用。
from seleniumbase import BaseCase
from parameterized import parameterized
class SearchTests(BaseCase):
"""搜索功能测试"""
@parameterized.expand([
("python", "Python 编程"),
("javascript", "JavaScript 教程"),
("selenium", "Selenium 自动化"),
])
def test_search(self, keyword, expected_result):
"""测试不同关键词的搜索结果"""
self.open("https://www.google.com")
# 在搜索框输入关键词
self.type("input[name='q']", keyword)
# 按回车搜索
self.enter("input[name='q']")
# 验证搜索结果包含期望内容
self.wait_for_text(expected_result, timeout=10)
如果没有安装 parameterized 库,需要先安装:
pip install parameterized
第四步:跨浏览器测试
确保你的网站在不同浏览器中都能正常工作是很重要的。SeleniumBase 可以轻松实现这一点:
import pytest
class CrossBrowserTests(BaseCase):
"""跨浏览器兼容性测试"""
@pytest.mark.parametrize("browser", ["chrome", "firefox", "edge"])
def test_homepage_loads(self, browser):
"""测试网站在不同浏览器中的加载情况"""
self.open("https://example.com")
# 验证页面基本元素存在
self.assert_element_present("header")
self.assert_element_present("footer")
self.assert_element_present("main")
# 验证页面标题
self.assert_true(len(self.get_page_title()) > 0)
运行特定浏览器的测试:
pytest test_file.py --browser=firefox
第五步:Page Object 模式
对于复杂的页面,SeleniumBase 推荐使用 Page Object 模式来组织代码。这种模式将页面元素和操作封装在单独的类中,使测试代码更加清晰和可维护。
首先,创建一个页面对象文件:
from seleniumbase import BaseCase
class LoginPage:
"""登录页面对象"""
def __init__(self, test_case):
"""初始化页面对象,传入测试实例"""
self.test = test_case
def open(self):
"""打开登录页面"""
self.test.open("https://practice.automationtesting.in/login/")
def enter_username(self, username):
"""输入用户名"""
self.test.type("#username", username)
def enter_password(self, password):
"""输入密码"""
self.test.type("#password", password)
def click_login(self):
"""点击登录按钮"""
self.test.click("[name='login']")
def login_as(self, username, password):
"""执行完整的登录流程"""
self.open()
self.enter_username(username)
self.enter_password(password)
self.click_login()
def get_error_message(self):
"""获取错误提示信息"""
return self.test.get_text(".woocommerce-error")
class DashboardPage:
"""仪表盘页面对象"""
def __init__(self, test_case):
self.test = test_case
def get_welcome_text(self):
"""获取欢迎文本"""
return self.test.get_text(".woocommerce-MyAccount-content p")
def is_logged_in(self):
"""检查是否已登录"""
return self.test.is_element_present(".woocommerce-MyAccount-content")
然后在测试中使用这些页面对象:
from seleniumbase import BaseCase
from pages.login_page import LoginPage
from pages.dashboard_page import DashboardPage
class LoginTests(BaseCase):
"""使用 Page Object 模式的登录测试"""
def test_successful_login(self):
"""测试成功登录"""
# 创建页面对象实例
login_page = LoginPage(self)
dashboard_page = DashboardPage(self)
# 执行登录
login_page.login_as("testuser@example.com", "TestPassword123!")
# 验证结果
self.wait_for_url_contains("account")
self.assert_true(dashboard_page.is_logged_in())
self.assert_in("Hello", dashboard_page.get_welcome_text())
def test_invalid_credentials(self):
"""测试无效凭据登录"""
login_page = LoginPage(self)
# 尝试使用错误密码登录
login_page.login_as("testuser@example.com", "wrongpassword")
# 验证错误提示
self.assert_text("ERROR", login_page.get_error_message())
这种模式的好处是:当页面结构改变时,你只需要修改页面对象类中的定位符,测试代码无需改动。同时,测试用例变得更加易读,更接近业务语言。
第六步:使用 Test Fixtures
pytest 的 fixtures 功能在 SeleniumBase 中同样适用,可以用来管理测试数据、共享配置等:
import pytest
from seleniumbase import BaseCase
@pytest.fixture
def test_data():
"""提供测试数据"""
return {
"valid_user": {
"email": "testuser@example.com",
"password": "TestPassword123!"
},
"invalid_user": {
"email": "invalid@example.com",
"password": "wrongpassword"
}
}
class DataDrivenTests(BaseCase):
"""使用 fixtures 的数据驱动测试"""
def test_login_with_valid_credentials(self, test_data):
"""使用有效凭据登录"""
user = test_data["valid_user"]
self.open("https://practice.automationtesting.in/login/")
self.type("#username", user["email"])
self.type("#password", user["password"])
self.click("[name='login']")
self.wait_for_url_contains("account")
def test_login_with_invalid_credentials(self, test_data):
"""使用无效凭据登录"""
user = test_data["invalid_user"]
self.open("https://practice.automationtesting.in/login/")
self.type("#username", user["email"])
self.type("#password", user["password"])
self.click("[name='login']")
self.wait_for_text("ERROR", timeout=5)
常见使用场景
场景一:电商网站功能测试
电商网站通常包含商品浏览、购物车、结算等多个流程,使用 SeleniumBase 可以轻松实现端到端测试:
from seleniumbase import BaseCase
class EcommerceTests(BaseCase):
"""电商网站端到端测试"""
def test_complete_purchase_flow(self):
"""测试完整的购买流程"""
# 访问网站首页
self.open("https://shop.example.com")
# 浏览商品
self.click(".category-electronics")
self.wait_for_text("电子产品", timeout=10)
# 添加商品到购物车
self.hover_and_click(".product-item:first-child", ".add-to-cart")
self.wait_for_text("已添加到购物车", timeout=5)
# 查看购物车
self.click(".cart-icon")
self.assert_element_present(".cart-item")
# 点击结算
self.click(".checkout-button")
# 填写收货信息
self.type("#shipping-name", "张三")
self.type("#shipping-address", "北京市朝阳区某街道123号")
self.type("#shipping-phone", "13800138000")
# 选择支付方式并下单
self.click("#payment-cod")
self.click(".place-order")
# 验证订单成功
self.wait_for_text("订单提交成功", timeout=10)
order_number = self.get_text(".order-number")
self.assert_true(order_number.startswith("ORD"))
场景二:表单验证测试
表单是 Web 应用中最常见的交互元素,完整的表单验证测试至关重要:
from seleniumbase import BaseCase
class FormValidationTests(BaseCase):
"""表单验证测试"""
def test_registration_form(self):
"""测试注册表单验证"""
self.open("https://example.com/register")
# 测试空表单提交
self.click("#submit")
self.assert_text("请填写用户名", "#username-error")
# 测试用户名格式验证
self.type("#username", "ab")
self.click("#submit")
self.assert_text("用户名至少需要3个字符", "#username-error")
# 测试邮箱格式验证
self.type("#username", "validuser")
self.type("#email", "notanemail")
self.click("#submit")
self.assert_text("请输入有效的邮箱地址", "#email-error")
# 测试密码强度验证
self.type("#email", "valid@email.com")
self.type("#password", "123")
self.click("#submit")
self.assert_text("密码至少需要8个字符", "#password-error")
# 测试密码确认不匹配
self.type("#password", "StrongPass123")
self.type("#confirm-password", "DifferentPass123")
self.click("#submit")
self.assert_text("两次输入的密码不一致", "#confirm-password-error")
# 测试有效数据提交
self.type("#confirm-password", "StrongPass123")
self.click("#submit")
self.wait_for_text("注册成功", timeout=10)
场景三:异步内容加载测试
现代 Web 应用大量使用 AJAX 和 JavaScript 动态加载内容,测试这类应用需要特殊处理:
from seleniumbase import BaseCase
class AsyncContentTests(BaseCase):
"""异步内容加载测试"""
def test_infinite_scroll(self):
"""测试无限滚动加载"""
self.open("https://example.com/news-feed")
# 初始加载的内容数量
initial_items = self.find_elements(".news-item")
initial_count = len(initial_items)
# 滚动到页面底部触发加载
self.scroll_to_bottom()
self.sleep(2) # 等待新内容加载
# 验证内容增加
new_items = self.find_elements(".news-item")
new_count = len(new_items)
self.assert_true(new_count > initial_count, "滚动后内容数量应该增加")
def test_lazy_loaded_images(self):
"""测试懒加载图片"""
self.open("https://example.com/gallery")
# 初始状态下,部分图片应该还在加载中
first_image_src = self.get_attribute(".gallery-image:first-child img", "src")
# 滚动到图片位置
self.scroll_to(".gallery-image:last-child")
self.sleep(1) # 等待图片加载
# 验证图片已完全加载(src 不再是占位符)
last_image_src = self.get_attribute(".gallery-image:last-child img", "src")
self.assert_false("placeholder" in last_image_src)
def test_dynamic_search_results(self):
"""测试动态搜索结果"""
self.open("https://example.com/search")
# 输入搜索关键词
self.type("#search-input", "python")
self.sleep(0.5) # 等待防抖
# 等待搜索结果显示
self.wait_for_element_visible(".search-results", timeout=10)
self.wait_for_ajax_calls_finished()
# 验证搜索结果
result_count = len(self.find_elements(".search-result"))
self.assert_true(result_count > 0, "应该显示搜索结果")
self.assert_in("python", self.get_text(".search-result:first-child").lower())
场景四:浏览器兼容性测试
确保应用在主流浏览器中都能正常运行:
import pytest
from seleniumbase import BaseCase
class BrowserCompatibilityTests(BaseCase):
"""浏览器兼容性测试"""
def test_form_rendering(self):
"""测试表单在各浏览器中的渲染"""
self.open("https://example.com/form")
# 验证所有表单项都可见
self.assert_element_visible("#username")
self.assert_element_visible("#email")
self.assert_element_visible("#password")
self.assert_element_visible("#submit-btn")
# 验证表单元素可以交互
self.type("#username", "testuser")
self.type("#email", "test@example.com")
self.type("#password", "SecurePass123!")
# 验证输入内容正确显示
self.assert_input_value("#username", "testuser")
self.assert_input_value("#email", "test@example.com")
self.assert_input_value("#password", "SecurePass123!")
def test_responsive_design(self):
"""测试响应式设计"""
# 桌面视图
self.open("https://example.com")
self.set_window_size(1920, 1080)
self.assert_element_visible(".desktop-nav")
# 平板视图
self.set_window_size(768, 1024)
self.sleep(0.5)
self.assert_element_visible(".tablet-nav")
# 移动端视图
self.set_window_size(375, 667)
self.sleep(0.5)
self.assert_element_present(".mobile-menu")
# 打开汉堡菜单
self.click(".menu-toggle")
self.assert_element_visible(".mobile-nav")
高级技巧与最佳实践
技巧一:自定义命令扩展基类
如果某些操作在项目中经常使用,可以创建自定义命令来减少重复代码:
from seleniumbase import BaseCase
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class ExtendedBaseCase(BaseCase):
"""扩展的 SeleniumBase 基类"""
def wait_for_loader(self, timeout=30):
"""等待页面加载器消失"""
try:
self.wait_for_element_not_visible(
".loader, .spinner, .loading-overlay",
timeout=timeout
)
except Exception:
pass # 如果没有加载器,忽略异常
def login(self, username, password):
"""统一的登录方法"""
self.open("https://example.com/login")
self.wait_for_loader()
self.type("#username", username)
self.type("#password", password)
self.click("#login-btn")
self.wait_for_loader()
self.wait_for_url_contains("/dashboard")
def logout(self):
"""统一的登出方法"""
self.click(".user-menu")
self.click(".logout-btn")
self.wait_for_url_contains("/login")
def is_logged_in(self):
"""检查用户是否已登录"""
return self.is_element_present(".user-menu")
def take_element_screenshot(self, selector, filename):
"""截取特定元素的屏幕截图"""
element = self.find_element(selector)
element.screenshot(filename)
def get_table_data(self, table_selector):
"""获取表格数据,返回二维列表"""
rows = self.find_elements(f"{table_selector} tr")
data = []
for row in rows:
cells = row.find_elements(By.TAG_NAME, "td")
if cells:
data.append([cell.text for cell in cells])
return data
然后让你的测试类继承这个扩展基类:
class MyTests(ExtendedBaseCase):
def test_something(self):
self.login("user@example.com", "password")
# 使用自定义的 login 方法
# ...
self.logout()
技巧二:测试报告增强
SeleniumBase 默认的报告已经很不错,但你可以通过配置让它更强大:
# 在 pytest.ini 或 setup.cfg 中配置
[pytest]
addopts =
--dashboard
--html=reports/report.html
--self-contained-html
--reruns=2
--tb=short
运行带详细报告的测试:
pytest tests/ --dashboard --html=reports/report.html --self-contained-html
生成的报告会自动包含:测试执行时间、失败截图、浏览器信息、控制台日志等。
技巧三:并行执行加速测试
测试套件变大后,执行时间会成为瓶颈。SeleniumBase 内置并行执行支持:
# 使用 4 个进程并行执行
pytest tests/ -n=4
# 分布式并行执行(需要安装 redis)
pytest tests/ -n=8 --dist=loadscope
并行执行时注意:确保测试之间相互独立,不要依赖执行顺序,使用唯一的数据避免冲突。
技巧四:处理验证码
自动化测试中处理验证码是个常见挑战,以下是几种实用方案:
from seleniumbase import BaseCase
class CaptchaTests(BaseCase):
"""验证码处理策略"""
def test_with_api_bypass(self):
"""方案一:通过 API 绕过验证"""
# 在测试环境中,可以直接调用后端 API 完成验证
import requests
captcha_token = self.solve_captcha_api()
requests.post("https://example.com/verify", data={"captcha": captcha_token})
def test_with_test_account(self):
"""方案二:使用测试账户"""
# 联系开发团队在测试环境中禁用验证码
self.open("https://test.example.com/login")
self.type("#username", "testbot@example.com")
self.type("#password", "TestBotPass123")
self.click("#login-btn")
# 无需处理验证码,直接登录成功
技巧五:处理 iframe 和多窗口
复杂的 Web 页面经常包含 iframe 和弹出窗口:
def test_iframe_interaction(self):
"""处理 iframe 交互"""
self.open("https://example.com/page-with-iframe")
# 切换到 iframe
self.switch_to_frame("#editor-frame")
# 在 iframe 中操作
self.type(".editor", "在 iframe 中输入的内容")
# 切换回主文档
self.switch_to_default_content()
# 验证主文档中的元素
self.assert_element_present(".main-content")
def test_multiple_windows(self):
"""处理多窗口场景"""
main_window = self.current_window_handle
# 点击打开新窗口的链接
self.click(".open-new-window")
self.wait_for_new_window()
# 切换到新窗口
new_window = [w for w in self.window_handles if w != main_window][0]
self.switch_to_window(new_window)
# 在新窗口中操作
self.type("#email-input", "test@example.com")
# 关闭新窗口,切回主窗口
self.close()
self.switch_to_window(main_window)
# 继续在主窗口操作
self.assert_text("Thanks for subscribing!", ".subscription-status")
技巧六:优雅的失败处理
当测试可能失败但你不想让整个测试套件中断时,可以使用软断言:
def test_multiple_checks(self):
"""执行多个检查,即使部分失败也继续"""
checks = [
("#header-logo", "Logo 应该可见"),
("#main-nav", "导航应该可见"),
("#footer-links", "页脚链接应该可见"),
("#copyright", "版权信息应该可见"),
]
failed_checks = []
for selector, message in checks:
try:
self.assert_element_visible(selector)
except AssertionError:
failed_checks.append(message)
self.save_screenshot(f"failed_{selector.replace('#', '')}.png")
# 最后报告所有失败
if failed_checks:
self.fail(f"以下检查失败:{', '.join(failed_checks)}")
技巧七:性能度量
测试不仅可以验证功能,还可以度量性能:
import time
from seleniumbase import BaseCase
class PerformanceTests(BaseCase):
"""性能测试"""
def test_page_load_time(self):
"""测量页面加载时间"""
start_time = time.time()
self.open("https://example.com")
load_time = time.time() - start_time
# 验证页面在 3 秒内加载完成
self.assert_true(
load_time < 3,
f"页面加载时间过长:{load_time:.2f}秒"
)
def test_api_response_time(self):
"""测量 API 响应时间"""
import requests
start_time = time.time()
response = requests.get("https://api.example.com/data")
api_time = time.time() - start_time
self.assert_equal(response.status_code, 200)
self.assert_true(
api_time < 1,
f"API 响应时间过长:{api_time:.2f}秒"
)
def test_element_interaction_speed(self):
"""测量元素交互响应速度"""
self.open("https://example.com/form")
start_time = time.time()
self.type("#search-input", "test query")
self.click("#search-btn")
self.wait_for_element_visible(".results", timeout=10)
interaction_time = time.time() - start_time
self.assert_true(
interaction_time < 2,
f"交互响应时间过长:{interaction_time:.2f}秒"
)
测试配置与维护
使用 YAML 管理测试配置
将测试配置与代码分离,便于管理和维护:
# config/test_config.yaml
environments:
dev:
base_url: "https://dev.example.com"
api_url: "https://api-dev.example.com"
staging:
base_url: "https://staging.example.com"
api_url: "https://api-staging.example.com"
prod:
base_url: "https://www.example.com"
api_url: "https://api.example.com"
test_data:
valid_user:
email: "test@example.com"
password: "TestPassword123!"
test_products:
- name: "测试商品1"
price: 99.00
- name: "测试商品2"
price: 199.00
timeouts:
default: 10
long_wait: 30
api_call: 5
在测试中读取配置:
import yaml
from seleniumbase import BaseCase
def load_config(env="dev"):
"""加载测试配置"""
with open("config/test_config.yaml", "r", encoding="utf-8") as f:
config = yaml.safe_load(f)
return config["environments"][env]
class ConfigBasedTests(BaseCase):
"""基于配置的测试"""
def setUp(self):
super().setUp()
self.config = load_config()
def test_example(self):
"""使用配置中的 URL"""
self.open(self.config["base_url"])
# ...
维护测试代码的建议
编写易于维护的测试代码是长期成功的关键。以下是一些实践建议:
保持测试独立:每个测试应该能够独立运行,不依赖其他测试的执行结果。使用 setUp 和 tearDown 方法来管理测试状态。
使用描述性的测试名称:测试方法名应该清晰表达测试意图。pytest 会直接使用方法名作为测试报告中的名称。
合理组织测试文件:按照功能模块或测试类型组织测试文件,便于查找和维护。
定期重构测试代码:随着应用迭代,测试代码也需要更新。移除过时的测试,更新定位符,保持测试的有效性。
监控测试稳定性:跟踪 flaky 测试(不稳定测试),及时修复或移除它们。
集成与扩展
与 CI/CD 集成
SeleniumBase 可以轻松集成到主流的持续集成系统中。
GitHub Actions 配置:
# .github/workflows/test.yml
name: Selenium Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
pip install seleniumbase
seleniumbase install chromium
- name: Run tests
run: |
pytest tests/ --dashboard --html=report.html
env:
BROWSER: chrome
- name: Upload test results
if: always()
uses: actions/upload-artifact@v3
with:
name: test-results
path: |
report.html
htmlcov/
Jenkins 配置:
pipeline {
agent any
stages {
stage('Test') {
steps {
sh '''
pip install seleniumbase
seleniumbase install chromium
pytest tests/ -n=4 --dashboard
'''
}
post {
always {
junit 'test-results/*.xml'
publishHTML target: [
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'htmlcov',
reportFiles: 'report.html',
reportName: 'Test Report'
]
}
}
}
}
}
与视觉回归测试集成
结合视觉回归测试可以发现 UI 意外变化:
from seleniumbase import BaseCase
from seleniumbase.plugins.enhanced_drivers import VisualTester
class VisualRegressionTests(BaseCase, VisualTester):
"""视觉回归测试"""
def test_homepage_visual(self):
"""检测首页视觉变化"""
self.open("https://example.com")
# 首次运行会保存基准截图
# 后续运行会与基准进行像素对比
self.assert视觉_match("homepage_base")
总结
SeleniumBase 将 Selenium WebDriver 的强大功能与实用的测试工具完美结合,为 Web 自动化测试提供了一个高效、可靠的解决方案。通过本文的详细介绍,你应该已经掌握了从环境搭建到高级技巧的完整知识体系。
核心要点回顾
SeleniumBase 简化了测试代码的编写,智能等待机制提高了测试稳定性,自动截图和报告功能让问题排查变得轻松,内置的并行执行能力大幅提升了测试效率,Page Object 模式让代码更易维护。掌握这些核心概念,你就能编写出高质量的自动化测试。
进一步学习资源
SeleniumBase 官方文档提供了最权威的参考资料:https://www.seleniumbase.io/
SeleniumBase 的 GitHub 仓库包含完整的源代码和示例:https://github.com/seleniumbase/SeleniumBase
pytest 官方文档帮助你深入了解测试框架的高级特性:https://docs.pytest.org/
立即开始你的 SeleniumBase 之旅,将自动化测试从负担转变为提升软件质量的有力武器。无论是个人项目还是团队协作,SeleniumBase 都能帮助你构建更可靠的 Web 应用。
评论区