Python自动化测试实战教程使用Selenium与Pytest进行Web应用测试插图

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模式组织代码

如果你想更进一步,我建议:

  1. 数据驱动:使用 @pytest.mark.parametrize 为同一个测试用例传入多组数据。
  2. 失败重试:安装 pytest-rerunfailures 插件,对不稳定的测试进行重试。
  3. 并行测试:使用 pytest-xdist 插件,大幅缩短测试套件的执行时间。
  4. 集成CI/CD:将测试命令写入GitHub Actions、Jenkins或GitLab CI的配置文件,实现提交代码后自动测试。

自动化测试是一个需要持续投入和优化的工程。一开始可能会觉得编写测试代码比手动测试还慢,但一旦形成规模,它带来的质量保障和回归效率是无可替代的。希望这篇实战教程能成为你构建自己测试堡垒的第一块基石。如果在实践中遇到问题,欢迎来源码库社区交流讨论,我们一起踩坑,一起进步!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。