Python+Pytest+Selenium自动化测试环境搭建与最佳实践指南

📅 2026/7/4 4:33:35
Python+Pytest+Selenium自动化测试环境搭建与最佳实践指南
1. 项目概述为什么选择这个技术栈如果你刚接触自动化测试或者想从零开始搭建一个稳定、可维护的测试环境那么“Python Pytest Selenium”这个组合绝对是你的不二之选。我从业十多年带过不少团队也踩过无数环境的坑最终沉淀下来的就是这个黄金三角。它不是什么高深莫测的黑科技但胜在简单、强大、生态好。Python的语法接近自然语言上手快Pytest是目前Python测试领域事实上的标准写用例就像写文档一样清晰Selenium则是浏览器自动化的老牌王者能模拟几乎所有用户操作。把它们仨组合起来你就能快速构建一个从元素定位到测试报告生成的全流程自动化框架。今天我就带你从零开始一步步搭建这个环境过程中我会分享那些官方文档里不会写的“坑”和“技巧”确保你一次成功少走弯路。2. 环境准备与核心工具安装搭建环境就像盖房子打地基基础不牢地动山摇。很多人失败就失败在第一步——环境没配好。我们按顺序来确保每一步都清晰无误。2.1 Python安装与配置避开第一个大坑Python是这一切的基石。首先去Python官网下载安装包。这里有个关键选择强烈建议选择Python 3.8至3.11之间的版本。版本太老如3.6可能缺少某些新库的支持版本太新如3.12有时会遇到第三方库兼容性问题作为生产环境稳定压倒一切。下载完成后运行安装程序。这里有一个99%的新手都会忽略但会导致后续无数报错的细节务必勾选“Add Python X.X to PATH”这个选项。它的作用是把Python和它的脚本工具目录添加到系统的环境变量里。如果不勾选你就得手动去配置对于新手来说极易出错。安装完成后我们需要验证一下。打开命令行Windows下按WinR输入cmdMac或Linux打开终端输入以下命令python --version如果正确显示Python版本号如Python 3.10.11恭喜你第一步成功了。如果显示“不是内部或外部命令”那就说明环境变量没配好。你需要手动添加右键“此电脑”-“属性”-“高级系统设置”-“环境变量”在“系统变量”里找到Path编辑新建两条分别指向你的Python安装目录例如C:\Users\YourName\AppData\Local\Programs\Python\Python310和它的Scripts目录例如C:\Users\YourName\AppData\Local\Programs\Python\Python310\Scripts。注意很多教程会教你用pip install来装包但pip这个命令本身就在Scripts目录下。如果没配好环境变量你连pip都用不了后续所有安装都无法进行。所以这一步是重中之重。2.2 使用虚拟环境项目隔离的必修课直接在本机Python环境里安装所有包是极其不推荐的。想象一下你同时做A、B两个项目A项目需要Selenium 4.10B项目需要Selenium 4.15全局安装只能有一个版本必然冲突。虚拟环境Virtual Environment就是为每个项目创建一个独立的Python运行沙盒互不干扰。创建虚拟环境非常简单。在你项目的根目录下比如你新建一个auto_test_project文件夹打开命令行执行python -m venv venv这个命令会在当前目录下创建一个名为venv的文件夹里面包含了一个独立的Python解释器和pip。接下来激活这个环境Windows:venv\Scripts\activateMac/Linux:source venv/bin/activate激活后你的命令行提示符前面会显示(venv)表示你已经进入了这个虚拟环境。之后所有pip install的操作都只会影响这个环境。实操心得我习惯把venv文件夹添加到.gitignore文件中不提交到代码仓库。每个开发者拉取代码后自己创建并激活虚拟环境再根据requirements.txt安装依赖这样可以保证团队环境一致。2.3 安装核心三件套Pytest, Selenium, WebDriver环境激活后我们就可以安装核心工具了。这里不建议一个个安装而是通过一个命令安装我们所需的基础套件。在激活的虚拟环境命令行中执行pip install pytest selenium pytest-html allure-pytest我来解释一下每个包的作用pytest: 测试框架本体用于组织、发现和运行测试用例。selenium: 浏览器自动化库提供了用代码控制浏览器的API。pytest-html: 一个Pytest插件用于生成简洁的HTML格式测试报告。allure-pytest: 另一个强大的报告插件能生成非常美观、详细的Allure报告可选但推荐。安装过程如果遇到网络慢或超时可以临时使用国内镜像源加速例如pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pytest selenium安装完成后可以验证一下pytest --version python -c import selenium; print(selenium.__version__)能正常输出版本号即可。最后也是最关键的一步下载浏览器驱动。Selenium本身只是一个“指挥者”它需要对应的“驾驶员”WebDriver来实际操控浏览器。以最常用的Chrome浏览器为例查看你电脑上Chrome的版本在浏览器地址栏输入chrome://settings/help。打开ChromeDriver官网或国内镜像站下载与你Chrome版本号完全一致的驱动。将下载的chromedriver.exeWindows文件放到一个你记得住的目录比如C:\WebDriver并将这个目录添加到系统的Path环境变量中。另一种更常见的做法是直接把chromedriver.exe放在你项目的根目录下然后在代码中指定它的路径。我推荐后者因为更利于项目移植。避坑指南浏览器驱动版本必须与浏览器版本匹配这是Selenium新手报错的重灾区。常见的错误信息是“This version of ChromeDriver only supports Chrome version XX”。如果找不到完全一致的版本可以尝试下载版本号最接近的。或者使用webdriver-manager这个库它可以自动下载和管理匹配的驱动省去手动操作的麻烦。安装命令是pip install webdriver-manager在代码中这样使用from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager driver webdriver.Chrome(serviceService(ChromeDriverManager().install()))3. 项目结构与第一个自动化脚本工具齐备现在我们来搭建一个清晰、可扩展的项目结构。混乱的目录是项目后期难以维护的罪魁祸首。3.1 构建清晰的项目目录一个好的目录结构能让你和你的团队事半功倍。我推荐如下结构auto_test_project/ ├── drivers/ # 存放浏览器驱动如果不用webdriver-manager ├── test_cases/ # 存放所有的测试用例文件 │ ├── __init__.py │ └── test_login.py # 示例测试模块 ├── page_objects/ # 页面对象模型PO模式目录 │ ├── __init__.py │ └── login_page.py # 示例页面类 ├── common/ # 公共模块 │ ├── __init__.py │ ├── base_page.py # 页面基类 │ └── logger.py # 日志模块 ├── reports/ # 测试报告输出目录 ├── conftest.py # Pytest的共享夹具配置 ├── pytest.ini # Pytest配置文件 └── requirements.txt # 项目依赖列表你可以使用tree命令需要安装或在IDE中手动创建这些文件夹和文件。其中__init__.py文件的作用是让Python将这个目录视为一个包Package从而可以导入其中的模块。3.2 编写第一个Selenium测试用例让我们从一个最简单的例子开始感受一下自动化测试的魔力。在test_cases目录下创建文件test_baidu_search.py。import pytest from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys import time class TestBaiduSearch: 一个简单的百度搜索测试用例 def setup_method(self): 每个测试方法开始前执行初始化浏览器 # 初始化Chrome浏览器驱动 self.driver webdriver.Chrome() # 窗口最大化 self.driver.maximize_window() # 设置隐式等待10秒。意思是查找元素时如果立即没找到会等待最多10秒期间不断重试。 self.driver.implicitly_wait(10) def teardown_method(self): 每个测试方法结束后执行关闭浏览器 # 等待3秒方便肉眼观察结果 time.sleep(3) # 关闭浏览器并退出驱动 self.driver.quit() def test_search_selenium(self): 测试搜索关键字‘Selenium’ # 1. 打开百度首页 self.driver.get(https://www.baidu.com) # 2. 定位搜索输入框。By.ID 表示通过HTML元素的id属性定位kw是百度搜索框的id。 search_box self.driver.find_element(By.ID, kw) # 3. 在搜索框中输入文本“Selenium” search_box.send_keys(Selenium) # 4. 模拟键盘按下回车键进行搜索 search_box.send_keys(Keys.RETURN) # 5. 断言验证搜索结果页面标题中包含“Selenium”这个词 assert Selenium in self.driver.title def test_search_pytest(self): 测试搜索关键字‘Pytest’ self.driver.get(https://www.baidu.com) search_box self.driver.find_element(By.ID, kw) search_box.send_keys(Pytest) search_box.send_keys(Keys.RETURN) assert Pytest in self.driver.title这个脚本定义了一个测试类TestBaiduSearch。setup_method和teardown_method是Pytest的夹具Fixtures的一种用法分别在每个测试方法test_开头执行前后运行用于初始化和清理。在测试方法里我们完成了“打开浏览器-访问网页-定位元素-操作元素-断言结果”的标准流程。3.3 使用Pytest运行并查看结果保存文件后在项目根目录auto_test_project下打开命令行确保虚拟环境已激活直接运行pytest test_cases/test_baidu_search.py -v-v参数表示输出详细信息。你会看到Pytest开始执行自动打开Chrome浏览器完成搜索操作然后关闭。命令行会输出类似以下的结果 test session starts platform win32 -- Python 3.10.11, pytest-7.4.0, pluggy-1.2.0 rootdir: C:\auto_test_project collected 2 items test_cases/test_baidu_search.py::TestBaiduSearch::test_search_selenium PASSED test_cases/test_baidu_search.py::TestBaiduSearch::test_search_pytest PASSED 2 passed in 15.23s 两个测试用例都通过了这就是你的第一个自动化测试。但现在的代码还很初级浏览器在每个用例都重启一次效率低且元素定位、业务逻辑和测试逻辑混杂在一起难以维护。接下来我们要引入更高级的模式和配置。4. 进阶配置与最佳实践要让这个框架真正用于项目我们需要解决效率、可维护性和报告可视化的问题。4.1 使用Conftest.py共享夹具上面的例子中每个测试类都要自己写setup_method和teardown_method很冗余。Pytest的conftest.py文件可以定义全局共享的夹具。在项目根目录创建conftest.py文件import pytest from selenium import webdriver from selenium.webdriver.chrome.options import Options pytest.fixture(scopeclass) def driver_init(request): 一个类级别的夹具为每个测试类提供唯一的driver实例。 scopeclass: 表示这个夹具在每个测试类中只执行一次初始化类中的所有测试方法共享同一个driver。 request: pytest内置参数可以访问请求该夹具的测试上下文。 # Chrome选项配置 chrome_options Options() # 无头模式不显示浏览器GUI在后台运行适合CI/CD环境。 # chrome_options.add_argument(--headless) # 禁用GPU加速在某些环境下可避免问题 chrome_options.add_argument(--disable-gpu) # 禁用沙箱在Docker或某些Linux系统上可能需要 chrome_options.add_argument(--no-sandbox) # 禁用DevShmUsage解决Linux下内存不足问题 chrome_options.add_argument(--disable-dev-shm-usage) # 初始化驱动并传入选项 driver webdriver.Chrome(optionschrome_options) driver.maximize_window() driver.implicitly_wait(10) # 将driver实例赋值给测试类的 driver 属性这样测试类中就能用 self.driver 访问了。 request.cls.driver driver # yield 之前是setup部分之后是teardown部分。yield将driver对象传递给测试函数。 yield # 所有测试执行完毕后关闭浏览器 driver.quit()然后修改我们的测试用例文件使用这个共享夹具import pytest from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys pytest.mark.usefixtures(driver_init) # 使用名为driver_init的夹具 class TestBaiduSearchAdvanced: 使用共享夹具的进阶测试用例 def test_search_selenium(self): 测试搜索关键字‘Selenium’ self.driver.get(https://www.baidu.com) search_box self.driver.find_element(By.ID, kw) search_box.send_keys(Selenium Keys.RETURN) assert Selenium in self.driver.title def test_search_pytest(self): 测试搜索关键字‘Pytest’ self.driver.get(https://www.baidu.com) search_box self.driver.find_element(By.ID, kw) search_box.send_keys(Pytest Keys.RETURN) assert Pytest in self.driver.title现在测试类简洁多了。pytest.mark.usefixtures(driver_init)这个装饰器告诉Pytest这个测试类要使用conftest.py中定义的driver_init夹具。scopeclass保证了同一个类里的多个测试方法共用同一个浏览器会话大大提升了执行速度。4.2 引入页面对象模型直接在测试用例里写find_element和send_keys是“脚本式”的写法一旦页面元素ID变了你需要修改所有用到它的测试用例。页面对象模型是解决这个问题的标准设计模式。它的核心思想是将页面的元素定位和操作封装成一个类测试用例只调用这个类的方法。首先在page_objects目录下创建baidu_page.pyfrom selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys class BaiduPage: 百度首页的页面对象类 # 将页面元素定位器集中管理像配置一样 URL https://www.baidu.com SEARCH_INPUT (By.ID, kw) # 搜索框 SEARCH_BUTTON (By.ID, su) # “百度一下”按钮 def __init__(self, driver): 初始化时需要传入driver对象 self.driver driver def load(self): 打开百度首页 self.driver.get(self.URL) def search(self, keyword): 执行搜索操作 :param keyword: 搜索关键词 # 定位元素并操作 search_box self.driver.find_element(*self.SEARCH_INPUT) # *用于解包元组 search_box.clear() # 先清空输入框是个好习惯 search_box.send_keys(keyword) # 这里可以点击按钮也可以按回车。我们按回车。 search_box.send_keys(Keys.RETURN) # 返回当前页面对象支持链式调用可选 return self然后修改测试用例使用页面对象import pytest from page_objects.baidu_page import BaiduPage pytest.mark.usefixtures(driver_init) class TestBaiduSearchWithPO: 使用页面对象模型的测试用例 def test_search_selenium(self): 使用PO模式搜索Selenium baidu_page BaiduPage(self.driver) baidu_page.load().search(Selenium) assert Selenium in self.driver.title def test_search_pytest(self): 使用PO模式搜索Pytest baidu_page BaiduPage(self.driver) baidu_page.load().search(Pytest) assert Pytest in self.driver.title看测试用例变得多么清晰它只关心业务逻辑打开百度搜索某个词而不关心具体怎么找到输入框、怎么点击。如果百度搜索框的ID明天从kw变成了searchInput你只需要去修改BaiduPage类中的SEARCH_INPUT这一个地方所有测试用例都无需改动。这就是PO模式带来的高可维护性。4.3 生成漂亮的测试报告自动化测试不能光看命令行输出我们需要直观的报告来展示测试结果。这里介绍两种最常用的报告插件。1. 生成HTML报告运行测试时添加--html参数pytest test_cases/ --htmlreports/report.html --self-contained-html--self-contained-html参数会将CSS样式内嵌到HTML文件中生成一个独立的报告文件。打开reports/report.html你就能看到一个包含通过率、执行时间、错误详情等信息的网页报告。2. 生成Allure报告更推荐Allure报告非常强大和美观。首先确保安装了allure-pytest。运行测试时需要指定一个目录来存放原始的Allure结果数据pytest test_cases/ --alluredirreports/allure-results运行完成后数据已经生成但需要Allure命令行工具来渲染成HTML报告。你需要先去Allure官网下载命令行工具并配置到系统Path。然后执行allure generate reports/allure-results -o reports/allure-report --clean allure open reports/allure-report最后一条命令会自动在浏览器中打开生成的Allure报告。报告里会有清晰的用例分类、步骤详情、截图需要额外配置、历史趋势图等非常专业。实操心得我通常会在conftest.py中配置自动截图当测试失败时自动截取当前页面并附加到Allure报告中。这能极大方便错误定位。配置代码如下import allure from datetime import datetime pytest.hookimpl(tryfirstTrue, hookwrapperTrue) def pytest_runtest_makereport(item, call): outcome yield rep outcome.get_result() if rep.when call and rep.failed: # 只有测试执行失败时才截图 driver item.cls.driver if hasattr(item.cls, driver) else None if driver: # 生成带时间戳的截图文件名 timestamp datetime.now().strftime(%Y%m%d_%H%M%S) screenshot_name fscreenshot_{item.name}_{timestamp}.png screenshot_path f./reports/screenshots/{screenshot_name} driver.save_screenshot(screenshot_path) # 将截图附加到Allure报告 allure.attach.file(screenshot_path, name失败截图, attachment_typeallure.attachment_type.PNG)5. 常见问题排查与性能优化即使环境搭好了在编写和运行脚本时你依然会遇到各种各样的问题。这里我总结了一些高频问题和优化技巧。5.1 元素定位失败问题大全这是Selenium自动化中最常见的问题没有之一。错误信息通常是NoSuchElementException。原因1页面未加载完成就进行定位。解决方案使用显式等待。这是比隐式等待更精确、更推荐的方式。它允许你为某个特定元素设置等待条件。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # 等待最多10秒直到ID为‘kw’的元素出现在DOM中并可见 wait WebDriverWait(driver, 10) search_box wait.until(EC.visibility_of_element_located((By.ID, kw))) search_box.send_keys(hello)expected_conditions模块提供了很多条件如元素可点击、元素存在、标题包含某文字等。原因2元素在iframe或shadow DOM内部。解决方案需要先切换到对应的iframe或shadow root。# 切换到iframe iframe driver.find_element(By.TAG_NAME, iframe) driver.switch_to.frame(iframe) # 操作iframe内的元素... # 操作完后切回主文档 driver.switch_to.default_content()原因3元素是动态生成的ID或Class每次都会变。解决方案使用相对定位方式如XPath或CSS Selector通过其文本内容、属性组合或层级关系来定位。# 通过部分文本定位链接 driver.find_element(By.XPATH, //a[contains(text(), 登录)]) # 通过属性组合定位 driver.find_element(By.CSS_SELECTOR, input[nameusername][typetext])注意XPath尽量少用绝对路径以/开头多用相对路径以//开头并避免使用索引如div[1]因为页面结构一变就容易失效。原因4页面有多个相同特征的元素定位到了第一个但不是你想要的那个。解决方案使用find_elements复数获取列表然后按索引或进一步筛选。buttons driver.find_elements(By.CLASS_NAME, submit-btn) if len(buttons) 1: buttons[1].click() # 点击第二个按钮5.2 测试执行稳定性与速度优化1. 使用无头模式在conftest.py的Chrome选项中取消注释--headless参数浏览器将在后台运行不显示GUI节省资源且速度更快特别适合在服务器或持续集成环境中运行。2. 优化等待策略减少/避免使用time.sleep()这是固定等待效率最低。尽量用显式等待代替。合理设置隐式等待时间一般10秒足够太长会拖慢失败用例的执行速度。混合使用显式与隐式等待Pytest官方不推荐同时用容易导致不可预知的超时。建议只使用显式等待并封装成通用方法。3. 使用PageFactory或BasePage封装通用操作创建一个BasePage类封装所有页面的通用操作如等待元素、点击、输入、截图等。让具体的页面类如BaiduPage继承它。# common/base_page.py from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class BasePage: def __init__(self, driver): self.driver driver self.wait WebDriverWait(driver, 10) def find_element(self, by, locator): 查找单个元素并等待其可见 return self.wait.until(EC.visibility_of_element_located((by, locator))) def click(self, by, locator): 点击元素 self.find_element(by, locator).click() def input_text(self, by, locator, text): 向元素输入文本 element self.find_element(by, locator) element.clear() element.send_keys(text)这样在具体的页面类中操作就变得更简洁、更健壮。5.3 配置管理与数据驱动1. 使用pytest.ini进行配置在项目根目录创建pytest.ini文件可以统一管理Pytest的默认行为。[pytest] # 自动发现测试文件的规则 testpaths test_cases # 匹配测试文件名的模式 python_files test_*.py # 匹配测试类名的模式 python_classes Test* # 匹配测试方法名的模式 python_functions test_* # 命令行默认参数 addopts -v --htmlreports/report.html --self-contained-html --alluredirreports/allure-results # 标记过滤器例如只运行标记为‘smoke’的用例 # markers # smoke: 冒烟测试配置好后在项目根目录直接运行pytest命令就会自动应用这些配置。2. 使用数据驱动测试同一个测试逻辑用多组数据来验证。Pytest的pytest.mark.parametrize装饰器非常好用。import pytest pytest.mark.parametrize(search_keyword, expected_title_part, [ (Selenium, Selenium), (Pytest, Pytest), (Python, Python), ]) def test_baidu_search_with_data(driver_init, search_keyword, expected_title_part): 数据驱动测试示例 driver driver_init driver.get(https://www.baidu.com) driver.find_element(By.ID, kw).send_keys(search_keyword Keys.RETURN) assert expected_title_part in driver.title这样你写一个测试函数就能运行三条测试用例分别验证不同的搜索词。测试数据和测试逻辑分离维护起来非常方便。6. 将项目融入持续集成流程个人学习或小团队使用在本地运行可能就够了。但对于稍正式的项目你需要把它接入持续集成/持续部署流水线实现代码提交后自动触发测试。这里以最流行的GitHub Actions为例给出一个最简单的配置。在项目根目录创建.github/workflows/ci.yml文件name: Python Selenium CI on: [push, pull_request] # 在代码推送或拉取请求时触发 jobs: test: runs-on: ubuntu-latest # 使用最新的Ubuntu系统作为运行环境 steps: - name: Checkout code uses: actions/checkoutv3 # 步骤1检出代码 - name: Set up Python uses: actions/setup-pythonv4 # 步骤2设置Python环境 with: python-version: 3.10 - name: Install dependencies run: | # 步骤3安装依赖 pip install -r requirements.txt pip install pytest selenium pytest-html allure-pytest webdriver-manager - name: Install Chrome and ChromeDriver run: | # 步骤4在Ubuntu系统上安装Chrome浏览器 sudo apt-get update sudo apt-get install -y google-chrome-stable - name: Run tests with Pytest run: | # 步骤5运行测试使用无头模式 # 设置一个环境变量告诉Chrome在无头模式下运行 export PYTEST_ADDOPTS--tbshort # 运行测试并生成报告 pytest test_cases/ --htmlreports/report.html --self-contained-html - name: Upload test report uses: actions/upload-artifactv3 # 步骤6上传生成的HTML报告作为工件 if: always() # 即使测试失败也上传报告 with: name: pytest-html-report path: reports/report.html这个工作流定义了每当有代码推送到仓库GitHub Actions就会启动一个Ubuntu虚拟机自动安装Python、项目依赖、Chrome浏览器然后以无头模式运行你的所有Pytest测试用例最后将HTML报告保存起来供你下载查看。这样自动化测试就真正成为了你开发流程中不可或缺的一环。走到这里你已经从一个零基础的新手成功搭建了一个具备工程化雏形的PythonPytestSelenium自动化测试环境。这个环境包含了虚拟隔离、页面对象模型、共享夹具、多种报告生成、常见问题解决方案以及CI/CD集成思路。记住框架是死的人是活的。在实际项目中你还需要根据业务特点不断封装更多通用组件如数据库操作、API调用、数据工厂等并建立完善的测试数据管理和用例组织规范。