ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python自动化测试实战:从Selenium到Pytest框架的完整指南

Python自动化测试实战:从Selenium到Pytest框架的完整指南 最近在带团队做自动化测试落地发现很多刚接触的同学对 Python 自动化测试的认知还停留在“用 Selenium 点点点”的阶段。实际上从环境搭建、脚本编写、框架设计到项目集成每一步都有不少细节和坑点。网上资料虽然多但要么太旧要么太散新手很难拼凑出一套能直接上手的完整体系。本文将以 2026 年主流技术栈为背景为你梳理一份从零开始的 Python 自动化测试实战指南。内容涵盖基础语法、核心工具Selenium、Pytest、接口测试、以及一个完整的 Web 项目实战。无论你是刚入门的小白还是想系统化提升的测试开发都能从中找到清晰的路径和可复用的代码。1. 自动化测试基础与核心概念在开始敲代码之前我们需要先统一思想理解自动化测试到底是什么以及为什么要做它。1.1 什么是自动化测试简单来说自动化测试就是利用软件工具和脚本自动执行测试用例、比较实际结果与预期结果并生成测试报告的过程。它的核心目标是替代或辅助人工完成那些重复、繁琐、耗时的测试任务。与手动测试相比自动化测试的优势非常明显效率高脚本可以7x24小时不间断运行执行速度远超人工。可重复每次执行的条件和步骤完全一致避免了人为误差。覆盖广可以轻松执行成千上万次的回归测试覆盖更多场景。快速反馈集成到CI/CD流程后能在代码提交后立即得到质量反馈。但它并非银弹其劣势在于初期成本高需要投入时间编写和维护脚本。无法替代探索性测试难以模拟人类的直觉和创造性思维。维护成本当被测应用界面或接口频繁变更时脚本需要同步更新。1.2 Python 在自动化测试中的优势Python 已成为自动化测试领域最主流的语言之一这得益于其诸多优点语法简洁代码可读性高学习曲线平缓非常适合测试人员快速上手。生态丰富拥有庞大而成熟的测试库生态如Selenium,Pytest,Requests,Appium等几乎覆盖所有测试类型。跨平台在 Windows、Linux、macOS 上都能良好运行。强大的社区支持遇到问题很容易找到解决方案和社区讨论。1.3 自动化测试的主要类型根据测试对象的不同自动化测试主要分为以下几类UI 自动化测试模拟用户操作浏览器或应用程序界面。常用工具是SeleniumWeb和Appium移动端。接口自动化测试直接测试系统的 API 接口不经过前端界面。这是当前性价比最高的自动化测试方式常用库是Requests。单元测试针对代码中最小的可测试单元通常是函数或方法进行测试。Python 内置unittest但更推荐Pytest。性能测试测试系统在高负载下的性能表现如Locust。本教程将重点讲解 UI 和接口自动化测试因为它们是测试工程师最常接触的领域。2. 环境准备与工具安装工欲善其事必先利其器。一个稳定、干净的开发环境是成功的第一步。2.1 Python 安装与环境配置步骤 1安装 Python访问 Python 官方网站下载最新稳定版本如 Python 3.11。安装时务必勾选“Add Python to PATH”这样可以在任何命令行中直接使用python命令。安装完成后打开终端Windows 用 CMD 或 PowerShellMac/Linux 用 Terminal输入以下命令验证python --version # 或 python3 --version如果正确显示版本号如Python 3.11.4说明安装成功。步骤 2使用虚拟环境强烈推荐虚拟环境可以为每个项目创建独立的 Python 包依赖避免项目间冲突。# 安装虚拟环境管理工具如果尚未安装 pip install virtualenv # 为你的自动化测试项目创建一个虚拟环境命名为 venv python -m venv venv # 激活虚拟环境 # Windows (CMD/PowerShell): venv\Scripts\activate # Mac/Linux: source venv/bin/activate激活后命令行提示符前通常会显示(venv)表示你已进入该虚拟环境。所有后续的包安装都只在此环境内生效。2.2 核心测试库安装在激活的虚拟环境中一次性安装我们后续需要的核心库pip install selenium pytest requests pytest-html allure-pytest openpyxlselenium: Web UI 自动化测试的核心库。pytest: 功能强大、灵活的测试框架用于组织和运行测试用例。requests: 用于发送 HTTP 请求是接口测试的基石。pytest-html: 生成 HTML 格式的测试报告。allure-pytest: 生成更美观、交互性更强的 Allure 测试报告可选但推荐。openpyxl: 用于读写 Excel 文件常用于管理测试数据。2.3 浏览器驱动管理Selenium 需要对应的浏览器驱动才能控制浏览器。以最常用的 Chrome 为例方法一手动下载推荐新手理解原理查看你的 Chrome 浏览器版本在浏览器地址栏输入chrome://version/。访问 ChromeDriver 官网下载与你的 Chrome主版本号完全一致的驱动。将下载的chromedriver.exe(Windows) 或chromedriver(Mac/Linux) 文件放在一个目录下如C:\WebDriver或/usr/local/bin。将该目录路径添加到系统的PATH环境变量中。方法二使用webdriver-manager自动管理推荐这是一个第三方库可以自动下载和匹配对应版本的驱动省去手动管理的麻烦。pip install webdriver-manager在代码中可以这样使用from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager # 自动下载并使用匹配的 ChromeDriver service Service(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice)2.4 IDE 选择VS Code推荐使用 Visual Studio Code 作为集成开发环境。它轻量、免费且通过插件拥有强大的 Python 开发支持。安装 VS Code。安装 Python 扩展Microsoft 官方发布。安装 Pytest 扩展便于在 IDE 内直接运行和调试测试用例。3. Python 自动化测试核心语法与 Pytest 框架掌握 Python 基础语法和 Pytest 框架是编写高效测试脚本的前提。3.1 必须掌握的 Python 语法点对于自动化测试你不需要成为 Python 专家但以下知识点必须牢固掌握变量与数据类型字符串、整数、列表、字典、元组、布尔值。控制流if-elif-else条件判断for和while循环。函数定义使用def定义函数理解参数和返回值。文件操作使用open()读写文本文件结合json或openpyxl处理结构化数据。异常处理使用try-except-finally来捕获和处理脚本运行中的异常增强脚本健壮性。模块导入使用import导入标准库或第三方库。3.2 Pytest 测试框架入门Pytest 是目前 Python 社区最主流的测试框架比内置的unittest更简洁、强大。1. 编写第一个测试用例创建一个名为test_sample.py的文件# test_sample.py def add(a, b): return a b def test_add(): assert add(2, 3) 5 assert add(-1, 1) 0 assert add(0, 0) 0 def test_list_contains(): my_list [1, 2, 3, 4] assert 3 in my_list assert 5 not in my_list规则测试文件以test_开头测试函数以test_开头。assert是断言语句如果条件为False则测试失败。2. 运行测试在终端中进入文件所在目录运行pytest test_sample.py -v-v参数表示输出详细信息。你会看到每个测试用例的执行结果.表示通过F表示失败。3. Pytest 核心特性Fixture用于提供测试所需的环境准备和清理工作如初始化数据库连接、打开浏览器。import pytest pytest.fixture def setup_data(): data {user: test, pass: 123} yield data # 测试函数执行前返回data print(测试结束清理数据) # 测试函数执行后执行 def test_login(setup_data): # 将fixture作为参数传入 assert setup_data[user] test参数化用一组数据驱动同一个测试逻辑避免写多个相似函数。import pytest pytest.mark.parametrize(a,b,expected, [ (2, 3, 5), (0, 0, 0), (-1, 1, 0), ]) def test_add_param(a, b, expected): assert a b expected标记Mark用于给测试用例分类例如只运行冒烟测试。import pytest pytest.mark.smoke def test_login_success(): # 冒烟测试用例 pass pytest.mark.regression def test_complex_feature(): # 回归测试用例 pass运行指定标记的测试pytest -m smoke4. Selenium Web UI 自动化实战我们将以一个简单的登录场景为例演示如何使用 Selenium 进行 Web 自动化测试。4.1 项目结构与基础配置首先创建项目目录结构python_auto_test/ ├── config/ │ └── config.py # 配置文件URL账号密码等 ├── pages/ │ └── login_page.py # 登录页面对象类 ├── testcases/ │ └── test_login.py # 测试用例 ├── reports/ # 测试报告目录 ├── conftest.py # Pytest 共享 fixture └── requirements.txt # 项目依赖在config.py中存放配置# config/config.py BASE_URL https://www.example.com/login # 替换为你的测试地址 USERNAME testuser PASSWORD testpass1234.2 实现 Page Object Model (POM)POM 是 UI 自动化的最佳设计模式将页面元素定位和操作封装成类使测试脚本更清晰、易维护。创建登录页面对象# pages/login_page.py from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class LoginPage: def __init__(self, driver): self.driver driver self.wait WebDriverWait(driver, 10) # 显式等待10秒 # 定位器 (Locators) USERNAME_INPUT (By.ID, username) PASSWORD_INPUT (By.ID, password) LOGIN_BUTTON (By.XPATH, //button[typesubmit]) ERROR_MSG (By.CLASS_NAME, alert-error) # 页面操作方法 def enter_username(self, username): element self.wait.until(EC.presence_of_element_located(self.USERNAME_INPUT)) element.clear() element.send_keys(username) def enter_password(self, password): self.driver.find_element(*self.PASSWORD_INPUT).clear() self.driver.find_element(*self.PASSWORD_INPUT).send_keys(password) def click_login(self): self.driver.find_element(*self.LOGIN_BUTTON).click() def get_error_message(self): try: return self.driver.find_element(*self.ERROR_MSG).text except: return None def login(self, username, password): 登录流程组合方法 self.enter_username(username) self.enter_password(password) self.click_login()4.3 编写测试用例与共享 Fixture在conftest.py中定义全局 Fixture用于管理浏览器的启动和关闭# conftest.py import pytest from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager pytest.fixture(scopefunction) # 每个测试函数执行一次 def driver(): # 自动管理驱动 service Service(ChromeDriverManager().install()) # 初始化浏览器添加常用选项 options webdriver.ChromeOptions() options.add_argument(--ignore-certificate-errors) options.add_argument(--start-maximized) # 最大化窗口 # options.add_argument(--headless) # 无头模式不打开浏览器窗口 driver webdriver.Chrome(serviceservice, optionsoptions) yield driver # 将driver对象提供给测试用例 # 测试结束后执行清理 driver.quit()编写具体的测试用例# testcases/test_login.py import pytest from pages.login_page import LoginPage from config.config import BASE_URL, USERNAME, PASSWORD class TestLogin: 登录功能测试类 def test_login_success(self, driver): 测试正常登录 driver.get(BASE_URL) login_page LoginPage(driver) login_page.login(USERNAME, PASSWORD) # 断言登录成功后应跳转到首页这里通过URL或页面特定元素判断 assert dashboard in driver.current_url # 或者 assert driver.find_element(By.ID, welcome-msg).is_displayed() def test_login_failure_with_wrong_password(self, driver): 测试密码错误 driver.get(BASE_URL) login_page LoginPage(driver) login_page.login(USERNAME, wrongpassword) error_msg login_page.get_error_message() # 断言应出现错误提示 assert error_msg is not None assert 密码错误 in error_msg or Invalid in error_msg pytest.mark.parametrize(username, password, [ (, testpass), # 用户名为空 (testuser, ), # 密码为空 (, ), # 都为空 ]) def test_login_failure_with_empty_credentials(self, driver, username, password): 参数化测试空用户名或密码登录 driver.get(BASE_URL) login_page LoginPage(driver) login_page.login(username, password) # 断言应仍在登录页面或有错误提示 assert BASE_URL in driver.current_url or login_page.get_error_message()4.4 运行测试并生成报告使用 Pytest 运行测试并生成 HTML 报告# 运行所有测试 pytest testcases/ -v # 运行特定标记的测试 pytest testcases/ -m smoke -v # 运行并生成HTML报告 pytest testcases/ --htmlreports/report.html --self-contained-html # 运行并生成Allure报告需先安装Allure命令行工具 pytest testcases/ --alluredirreports/allure_results allure serve reports/allure_results # 生成并打开本地报告5. Requests 接口自动化测试实战接口测试不依赖 UI执行更快、更稳定是自动化测试的核心。5.1 接口测试基础与 Requests 库我们将测试一个典型的 RESTful API。假设我们有一个用户管理接口。首先了解基本的 HTTP 方法GET 获取资源。POST 创建资源。PUT/PATCH 更新资源。DELETE 删除资源。使用requests库发送请求import requests import json # 定义一个基础的API测试类 class UserApiClient: def __init__(self, base_url): self.base_url base_url self.session requests.Session() # 使用Session保持会话如cookie self.session.headers.update({ Content-Type: application/json, User-Agent: PythonAutoTest/1.0 }) def get_user(self, user_id): GET /users/{id} url f{self.base_url}/users/{user_id} response self.session.get(url) return response # 返回响应对象便于后续断言 def create_user(self, user_data): POST /users url f{self.base_url}/users # 将字典数据转换为JSON字符串 json_data json.dumps(user_data) response self.session.post(url, datajson_data) return response def update_user(self, user_id, update_data): PUT /users/{id} url f{self.base_url}/users/{user_id} response self.session.put(url, jsonupdate_data) # 使用json参数自动处理 return response def delete_user(self, user_id): DELETE /users/{id} url f{self.base_url}/users/{user_id} response self.session.delete(url) return response5.2 编写接口测试用例创建test_api_user.py# testcases/test_api_user.py import pytest from api_client.user_client import UserApiClient # 假设我们将上面的类放在这里 import json BASE_URL https://api.example.com/v1 # 替换为你的测试接口地址 pytest.fixture(scopeclass) def api_client(): 提供API客户端Fixture client UserApiClient(BASE_URL) yield client # 测试类结束后可以做一些清理比如删除测试创建的用户 class TestUserApi: 用户接口测试套件 def test_get_user_success(self, api_client): 测试获取已存在的用户 # 假设已知ID为1的用户存在 response api_client.get_user(1) # 断言状态码 assert response.status_code 200 # 断言响应体结构 user_data response.json() assert id in user_data assert name in user_data assert user_data[id] 1 def test_get_user_not_found(self, api_client): 测试获取不存在的用户 response api_client.get_user(99999) # 不存在的ID assert response.status_code 404 def test_create_and_delete_user(self, api_client): 测试创建用户并清理集成测试 # 1. 创建用户 new_user {name: AutoTestUser, email: autotestexample.com} create_resp api_client.create_user(new_user) assert create_resp.status_code 201 # 创建成功通常是201 created_user create_resp.json() user_id created_user[id] assert created_user[name] new_user[name] # 2. 验证用户已创建 get_resp api_client.get_user(user_id) assert get_resp.status_code 200 # 3. 清理删除用户 delete_resp api_client.delete_user(user_id) assert delete_resp.status_code in [200, 204] # 删除成功 # 4. 验证用户已删除 get_resp_after_delete api_client.get_user(user_id) assert get_resp_after_delete.status_code 4045.3 数据驱动测试将测试数据与测试逻辑分离是提高脚本可维护性的关键。我们可以使用 JSON 或 Excel 文件来管理测试数据。使用 JSON 文件创建test_data/login_data.json[ { case_name: 正确账号密码, username: admin, password: admin123, expected_status: 200, expected_msg: 登录成功 }, { case_name: 错误密码, username: admin, password: wrong, expected_status: 401, expected_msg: 用户名或密码错误 } ]在测试中读取import json import pytest def load_test_data(file_path): with open(file_path, r, encodingutf-8) as f: return json.load(f) pytest.mark.parametrize(data, load_test_data(test_data/login_data.json)) def test_login_data_driven(api_client, data): # 使用 data 字典中的字段进行测试和断言 # ... 测试逻辑 ... pass6. 项目实战搭建一个简易的自动化测试框架现在我们将前面学到的知识整合起来搭建一个结构清晰、易于维护的小型自动化测试框架。6.1 框架设计目标可配置通过配置文件管理环境、账号、日志级别等。数据驱动测试数据与脚本分离。页面对象UI 测试使用 POM 模式。日志与报告自动生成清晰的测试日志和可视化报告。用例组织使用 Pytest 高效组织和管理用例。6.2 最终项目结构python_auto_test_framework/ ├── common/ # 公共模块 │ ├── __init__.py │ ├── base_page.py # 页面基类封装通用方法 │ ├── logger.py # 日志记录器 │ └── webdriver_factory.py # 浏览器工厂支持多浏览器 ├── config/ # 配置层 │ ├── __init__.py │ ├── config.yaml # YAML格式配置文件推荐 │ └── constants.py # 常量定义 ├── pages/ # 页面对象层 │ ├── __init__.py │ ├── login_page.py │ └── home_page.py ├── testcases/ # 测试用例层 │ ├── __init__.py │ ├── web_ui/ │ │ ├── __init__.py │ │ └── test_login.py │ └── api/ │ ├── __init__.py │ └── test_user_api.py ├── test_data/ # 测试数据层 │ ├── __init__.py │ ├── users.json │ └── ui_data.xlsx ├── reports/ # 报告输出目录 │ └── .gitkeep ├── logs/ # 日志输出目录 │ └── .gitkeep ├── conftest.py # Pytest根目录配置全局Fixture ├── pytest.ini # Pytest配置文件 ├── requirements.txt # 项目依赖 └── README.md # 项目说明6.3 核心模块代码示例1. 配置文件 (config/config.yaml)# config/config.yaml env: test # 环境test, staging, prod test: base_url: https://test.example.com api_url: https://api.test.example.com/v1 username: test_user password: test_pass_123 staging: base_url: https://staging.example.com api_url: https://api.staging.example.com/v1 logging: level: INFO file: logs/automation.log format: %(asctime)s - %(name)s - %(levelname)s - %(message)s browser: name: chrome # chrome, firefox, edge headless: false implicit_wait: 102. 配置读取工具# config/__init__.py import os import yaml def load_config(): config_path os.path.join(os.path.dirname(__file__), config.yaml) with open(config_path, r, encodingutf-8) as f: config yaml.safe_load(f) env config.get(env, test) # 返回当前环境的配置 env_config config.get(env, {}) # 将通用配置如logging也合并进去 env_config[logging] config.get(logging, {}) env_config[browser] config.get(browser, {}) return env_config # 导出一个全局配置对象 CONF load_config()3. 基础页面类 (common/base_page.py)# common/base_page.py from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException from common.logger import get_logger log get_logger(__name__) class BasePage: def __init__(self, driver): self.driver driver self.wait WebDriverWait(driver, 10) self.log log def find_element(self, locator, timeout10): 查找单个元素带显式等待 try: element WebDriverWait(self.driver, timeout).until( EC.presence_of_element_located(locator) ) return element except TimeoutException: self.log.error(f元素未找到: {locator}) raise def click(self, locator): 点击元素 element self.find_element(locator) element.click() self.log.info(f点击元素: {locator}) def input_text(self, locator, text): 输入文本 element self.find_element(locator) element.clear() element.send_keys(text) self.log.info(f在元素 {locator} 输入: {text}) def get_text(self, locator): 获取元素文本 element self.find_element(locator) return element.text4. 日志模块 (common/logger.py)# common/logger.py import logging import os from config import CONF def get_logger(name): logger logging.getLogger(name) if logger.hasHandlers(): return logger logger.setLevel(getattr(logging, CONF[logging][level])) # 控制台处理器 ch logging.StreamHandler() ch_formatter logging.Formatter(CONF[logging][format]) ch.setFormatter(ch_formatter) logger.addHandler(ch) # 文件处理器 log_file CONF[logging][file] os.makedirs(os.path.dirname(log_file), exist_okTrue) fh logging.FileHandler(log_file, encodingutf-8) fh_formatter logging.Formatter(CONF[logging][format]) fh.setFormatter(fh_formatter) logger.addHandler(fh) return logger6.4 运行与集成1. Pytest 配置文件 (pytest.ini)[pytest] # 指定测试文件路径 testpaths testcases # 自动发现测试文件规则 python_files test_*.py python_classes Test* python_functions test_* # 添加命令行默认选项 addopts -v --htmlreports/report.html --self-contained-html --capturetee-sys # 标记定义 markers smoke: 冒烟测试 regression: 回归测试 ui: UI测试 api: 接口测试2. 依赖文件 (requirements.txt)selenium4.10.0 pytest7.4.0 requests2.31.0 pytest-html4.0.0 allure-pytest2.12.0 webdriver-manager4.0.0 PyYAML6.0 openpyxl3.1.0现在你可以在项目根目录下运行一条命令执行所有测试并生成报告pytest7. 常见问题与排查思路在自动化测试实践中你会遇到各种各样的问题。下面是一些典型问题的排查思路。问题现象可能原因排查步骤与解决方案ModuleNotFoundError: No module named ‘xxx’1. 模块未安装。2. 虚拟环境未激活或不对。3. IDE 使用的解释器路径错误。1. 使用pip list检查模块是否安装。2. 确认终端已激活正确的虚拟环境命令行前有(venv)。3. 在 VS Code 中按F1选择Python: Select Interpreter指向虚拟环境下的python.exe。Selenium 报错WebDriverException: Message: ‘chromedriver’ executable needs to be in PATH1. ChromeDriver 未安装或路径不对。2. Chrome 浏览器版本与 ChromeDriver 版本不匹配。1.推荐使用webdriver-manager自动管理驱动。2. 手动检查 Chrome 版本下载对应版本的 ChromeDriver并确保其所在目录已添加到系统PATH。元素找不到NoSuchElementException1. 元素定位符Locator写错。2. 页面尚未加载完成。3. 元素在 iframe 内。4. 元素是动态生成的。1. 使用浏览器开发者工具F12重新检查元素属性。2. 添加显式等待WebDriverWait等待元素出现。3. 使用driver.switch_to.frame()切换到 iframe。4. 使用更稳定的定位方式如>测试用例通过但实际功能有问题1. 断言条件过于宽松或不准确。2. 测试数据问题。3. 异步操作未等待完成。1. 审查断言逻辑确保它能准确验证业务规则。2. 检查测试数据是否与预期一致。3. 在操作后添加适当的等待如等待某个成功状态元素出现。接口测试返回 4xx/5xx 错误1. 请求 URL、方法、参数错误。2. 缺少必要的请求头如Authorization,Content-Type。3. 服务器内部错误。1. 使用 Postman 等工具先手动验证接口是否正常。2. 检查代码中的请求构造对比抓包F12 Network看到的正确请求。3. 查看响应体中的具体错误信息。测试报告为空或格式错乱1. 报告生成路径错误。2. Pytest 运行过程中发生异常导致提前退出。3. 样式文件丢失非自包含报告。1. 检查--html参数指定的路径是否正确。2. 确保测试用例能正常执行完毕使用try-except捕获可能的中断异常。3. 使用--self-contained-html参数生成独立的 HTML 报告。8. 最佳实践与工程建议遵循以下最佳实践能让你的自动化测试项目更加健壮、可维护。保持用例独立性每个测试用例都应该是自包含的不依赖其他用例的执行顺序或状态。使用setup/teardown(Fixtures) 来准备和清理测试环境。使用明确的等待避免硬编码sleeptime.sleep(5)是脆弱的网络或机器慢可能导致等待不足。始终使用 Selenium 的显式等待(WebDriverWait) 来等待特定条件成立。为元素定义有意义的定位符优先使用id、name其次使用css selector或xpath。与开发约定使用>
返回列表