
Python自动化测试实战:用Selenium与Pytest为你的Web应用保驾护航
你好,我是源码库的一名技术博主。在多年的Web开发与测试工作中,我深刻体会到,一个稳定、可重复的自动化测试体系,是项目质量和开发效率的生命线。今天,我想和你分享的,正是如何用Python生态中的两大王牌工具——Selenium和Pytest,搭建一套高效、易维护的Web自动化测试框架。这不是一个纸上谈兵的教程,而是我踩过无数坑后总结出的实战经验,希望能帮你少走弯路。
一、环境搭建:万事开头,先利其器
首先,我们需要一个干净、隔离的Python环境。我强烈推荐使用虚拟环境,它能避免不同项目间的依赖冲突。让我们从创建项目目录开始。
# 创建项目目录并进入
mkdir selenium_pytest_demo && cd selenium_pytest_demo
# 创建虚拟环境(这里使用venv,你也可以用conda或virtualenv)
python3 -m venv venv
# 激活虚拟环境
# 在Windows上:venvScriptsactivate
# 在Mac/Linux上:source venv/bin/activate
# 安装核心依赖
pip install selenium pytest pytest-html
踩坑提示:Selenium需要浏览器驱动。以最常用的Chrome为例,你需要下载与你的Chrome浏览器版本匹配的 chromedriver,并将其放在系统PATH路径下,或者直接在代码中指定路径。版本不匹配是新手最常见的错误之一!你可以去 ChromeDriver官网下载。
二、第一个测试用例:让浏览器动起来
理论说再多,不如一行代码。让我们写一个最简单的测试,打开百度并搜索“源码库”。我会将浏览器驱动放在项目根目录下,方便管理。
# test_baidu_search.py
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
def test_baidu_search():
# 初始化驱动,指定chromedriver路径
driver = webdriver.Chrome(executable_path='./chromedriver') # Windows可能是'./chromedriver.exe'
driver.maximize_window() # 最大化窗口,避免元素被遮挡
try:
# 1. 打开百度
driver.get("https://www.baidu.com")
time.sleep(2) # 等待页面加载,生产环境应使用更智能的等待
# 2. 定位搜索框并输入关键词
search_box = driver.find_element(By.ID, 'kw')
search_box.send_keys("源码库")
search_box.send_keys(Keys.RETURN) # 模拟回车键
# 3. 等待结果加载并断言
time.sleep(3)
assert "源码库" in driver.title
print("测试通过!页面标题包含‘源码库’")
finally:
# 4. 无论测试成功与否,都关闭浏览器
driver.quit()
if __name__ == "__main__":
test_baidu_search()
运行这个脚本,你会看到浏览器自动完成了一系列操作。但这里用了 time.sleep,这是一种“硬等待”,效率低下且不可靠。在实际项目中,我们必须用“显式等待”。
三、引入Pytest与高级等待:构建健壮的测试
Pytest让测试的组织、运行和断言变得异常优雅。我们来重构上面的测试,使用Pytest的测试函数格式,并引入Selenium的 WebDriverWait。
# test_baidu_with_pytest.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
# 使用pytest fixture来管理driver的生命周期
@pytest.fixture(scope="function")
def driver():
# 初始化
_driver = webdriver.Chrome(executable_path='./chromedriver')
_driver.maximize_window()
yield _driver # 将driver对象提供给测试函数使用
# 清理
_driver.quit()
def test_search_yuanku(driver):
"""测试在百度搜索‘源码库’"""
wait = WebDriverWait(driver, 10) # 创建等待对象,最多等10秒
driver.get("https://www.baidu.com")
# 显式等待:直到搜索框出现并可交互
search_input = wait.until(
EC.element_to_be_clickable((By.ID, "kw"))
)
search_input.send_keys("源码库n") # n 也可以代替回车
# 显式等待:直到搜索结果的第一条链接出现
first_result = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, "#content_left h3 a"))
)
# Pytest风格的断言
assert "源码库" in driver.title
assert first_result.text # 断言第一条结果有文本内容
print(f"第一条结果标题是:{first_result.text}")
现在,在项目根目录下运行 pytest test_baidu_with_pytest.py -v,你会看到更清晰的测试报告。使用 fixture 管理资源,代码复用性和可读性大大提升。WebDriverWait 则让测试在元素出现后才进行操作,避免了因网络或性能问题导致的失败。
四、项目结构优化:迈向可维护的测试框架
当测试用例越来越多时,我们需要一个清晰的结构。一个我常用的目录结构如下:
selenium_pytest_project/
├── config/ # 配置文件
│ └── settings.py # 定义URL、超时时间等常量
├── pages/ # 页面对象模型(Page Object Model, POM)
│ └── baidu_page.py
├── tests/ # 测试用例
│ └── test_baidu.py
├── conftest.py # Pytest的共享fixture和钩子函数
├── requirements.txt # 项目依赖
└── chromedriver # 浏览器驱动
核心思想是POM模式:将页面元素定位和操作封装成类,测试脚本只调用业务方法。这能极大降低元素定位变更对测试脚本的影响。
让我们实现一个简单的 baidu_page.py:
# pages/baidu_page.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class BaiduPage:
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
# 元素定位器
SEARCH_INPUT = (By.ID, 'kw')
SEARCH_BUTTON = (By.ID, 'su')
def open(self):
self.driver.get("https://www.baidu.com")
return self
def search(self, keyword):
"""输入关键词并搜索"""
search_box = self.wait.until(
EC.element_to_be_clickable(self.SEARCH_INPUT)
)
search_box.clear()
search_box.send_keys(keyword)
self.driver.find_element(*self.SEARCH_BUTTON).click()
return self # 支持链式调用
然后在 conftest.py 中定义全局的 driver fixture:
# conftest.py
import pytest
from selenium import webdriver
@pytest.fixture(scope="session") # 整个测试会话只启动一次浏览器
def driver():
_driver = webdriver.Chrome(executable_path='./chromedriver')
_driver.maximize_window()
yield _driver
_driver.quit()
最后,优雅的测试用例是这样的:
# tests/test_baidu.py
from pages.baidu_page import BaiduPage
def test_search_with_pom(driver):
"""使用POM模式进行搜索测试"""
page = BaiduPage(driver)
page.open().search("Selenium自动化测试")
# 断言可以放在这里,或者Page对象里也可以有获取结果的方法
assert "Selenium" in driver.title
运行所有测试:pytest tests/ -v --html=report.html。加上 --html 参数,Pytest会生成一个漂亮的HTML测试报告,非常适合集成到CI/CD流程中。
五、总结与进阶建议
至此,我们已经搭建了一个具备基本框架、使用POM模式、并支持生成报告的自动化测试项目。回顾一下关键点:使用虚拟环境隔离依赖、用WebDriverWait替代硬等待、用Pytest fixture管理测试资源、用POM模式组织代码。
如果你想更进一步,我建议:
- 数据驱动:使用
@pytest.mark.parametrize为同一个测试用例传入多组数据。 - 失败重试:安装
pytest-rerunfailures插件,对不稳定的测试进行重试。 - 并行测试:使用
pytest-xdist插件,大幅缩短测试套件的执行时间。 - 集成CI/CD:将测试命令写入GitHub Actions、Jenkins或GitLab CI的配置文件,实现提交代码后自动测试。
自动化测试是一个需要持续投入和优化的工程。一开始可能会觉得编写测试代码比手动测试还慢,但一旦形成规模,它带来的质量保障和回归效率是无可替代的。希望这篇实战教程能成为你构建自己测试堡垒的第一块基石。如果在实践中遇到问题,欢迎来源码库社区交流讨论,我们一起踩坑,一起进步!

评论(0)