ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Python自动化测试完整教程:pytest + selenium实战

Python自动化测试完整教程:pytest + selenium实战 目录前言环境搭建pytest基础教程selenium基础教程pytest selenium实战项目页面对象模式(POM)测试报告生成持续集成配置最佳实践和进阶技巧总结前言自动化测试是现代软件开发中不可或缺的一环。Python作为一门简洁优雅的编程语言配合pytest测试框架和selenium自动化工具为我们提供了强大的自动化测试解决方案。本教程将从零开始带领大家掌握Python自动化测试的核心技能通过实战项目学会如何构建稳定、高效的自动化测试体系。环境搭建安装Python和依赖包# 创建虚拟环境 python -m venv test_env source test_env/bin/activate # Windows: test_env\Scripts\activate # 安装核心依赖 pip install pytest selenium webdriver-manager pytest-html allure-pytest浏览器驱动配置# 使用webdriver-manager自动管理驱动 from webdriver_manager.chrome import ChromeDriverManager from selenium import webdriver from selenium.webdriver.chrome.service import Service def setup_driver(): service Service(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice) return driver项目结构搭建automation_project/ ├── tests/ │ ├── __init__.py │ ├── test_login.py │ └── test_search.py ├── pages/ │ ├── __init__.py │ ├── base_page.py │ └── login_page.py ├── utils/ │ ├── __init__.py │ └── config.py ├── drivers/ ├── reports/ ├── conftest.py ├── pytest.ini └── requirements.txtpytest基础教程pytest核心概念pytest是Python中最流行的测试框架具有以下特点简单易用的断言语法丰富的插件生态系统强大的fixture机制灵活的测试发现和执行基础测试示例# test_basic.py import pytest def test_simple_assert(): 基础断言测试 assert 1 1 2 def test_string_operations(): 字符串操作测试 text Hello, World! assert Hello in text assert text.startswith(Hello) assert text.endswith(!) class TestCalculator: 测试类示例 def test_addition(self): assert 2 3 5 def test_division(self): assert 10 / 2 5 def test_division_by_zero(self): with pytest.raises(ZeroDivisionError): 10 / 0fixture机制深入# conftest.py import pytest from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager pytest.fixture(scopesession) def driver(): 会话级别的浏览器驱动 options webdriver.ChromeOptions() options.add_argument(--headless) # 无头模式 driver webdriver.Chrome( serviceService(ChromeDriverManager().install()), optionsoptions ) yield driver driver.quit() pytest.fixture def test_data(): 测试数据fixture return { username: testexample.com, password: password123 }参数化测试# test_parametrize.py import pytest pytest.mark.parametrize(a,b,expected, [ (2, 3, 5), (1, 1, 2), (0, 5, 5), (-1, 1, 0) ]) def test_addition(a, b, expected): assert a b expected pytest.mark.parametrize(url, [ https://www.baidu.com, https://www.google.com ]) def test_website_accessibility(driver, url): driver.get(url) assert driver.titleselenium基础教程元素定位策略from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class ElementLocator: 元素定位封装类 def __init__(self, driver): self.driver driver self.wait WebDriverWait(driver, 10) def find_element_safely(self, locator): 安全查找元素 try: element self.wait.until(EC.presence_of_element_located(locator)) return element except TimeoutException: print(f元素定位失败: {locator}) return None def click_element(self, locator): 点击元素 element self.wait.until(EC.element_to_be_clickable(locator)) element.click() def input_text(self, locator, text): 输入文本 element self.find_element_safely(locator) if element: element.clear() element.send_keys(text)常用操作封装# utils/selenium_helper.py from selenium.webdriver.support.ui import Select from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys class SeleniumHelper: Selenium操作助手类 def __init__(self, driver): self.driver driver def scroll_to_element(self, element): 滚动到指定元素 self.driver.execute_script(arguments[0].scrollIntoView();, element) def select_dropdown_by_text(self, locator, text): 通过文本选择下拉框 select Select(self.driver.find_element(*locator)) select.select_by_visible_text(text) def hover_element(self, element): 鼠标悬停 actions ActionChains(self.driver) actions.move_to_element(element).perform() def switch_to_iframe(self, iframe_locator): 切换到iframe iframe self.driver.find_element(*iframe_locator) self.driver.switch_to.frame(iframe) def take_screenshot(self, filename): 截图 self.driver.save_screenshot(fscreenshots/{filename})pytest selenium实战项目实战项目电商网站测试让我们以一个电商网站为例构建完整的自动化测试项目。配置文件设置# utils/config.py class Config: 测试配置类 BASE_URL https://example-shop.com TIMEOUT 10 BROWSER chrome HEADLESS False # 测试账户信息 TEST_USER { email: testexample.com, password: password123 } # 测试数据 TEST_PRODUCT { name: iPhone 14, price: 999.99 }基础页面类# pages/base_page.py from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By class BasePage: 基础页面类 def __init__(self, driver): self.driver driver self.wait WebDriverWait(driver, 10) def open(self, url): 打开页面 self.driver.get(url) def find_element(self, locator): 查找元素 return self.wait.until(EC.presence_of_element_located(locator)) def click(self, locator): 点击元素 element self.wait.until(EC.element_to_be_clickable(locator)) element.click() def input_text(self, locator, text): 输入文本 element self.find_element(locator) element.clear() element.send_keys(text) def get_text(self, locator): 获取元素文本 element self.find_element(locator) return element.text def is_element_visible(self, locator): 检查元素是否可见 try: self.wait.until(EC.visibility_of_element_located(locator)) return True except: return False登录页面类# pages/login_page.py from selenium.webdriver.common.by import By from pages.base_page import BasePage class LoginPage(BasePage): 登录页面 # 页面元素定位 EMAIL_INPUT (By.ID, email) PASSWORD_INPUT (By.ID, password) LOGIN_BUTTON (By.XPATH, //button[typesubmit]) ERROR_MESSAGE (By.CLASS_NAME, error-message) SUCCESS_MESSAGE (By.CLASS_NAME, success-message) def login(self, email, password): 执行登录操作 self.input_text(self.EMAIL_INPUT, email) self.input_text(self.PASSWORD_INPUT, password) self.click(self.LOGIN_BUTTON) def get_error_message(self): 获取错误信息 if self.is_element_visible(self.ERROR_MESSAGE): return self.get_text(self.ERROR_MESSAGE) return None def is_login_successful(self): 检查登录是否成功 return self.is_element_visible(self.SUCCESS_MESSAGE)登录测试用例# tests/test_login.py import pytest from pages.login_page import LoginPage from utils.config import Config class TestLogin: 登录功能测试类 pytest.fixture(autouseTrue) def setup(self, driver): 测试前置条件 self.driver driver self.login_page LoginPage(driver) self.login_page.open(f{Config.BASE_URL}/login) def test_valid_login(self): 测试有效登录 self.login_page.login( Config.TEST_USER[email], Config.TEST_USER[password] ) assert self.login_page.is_login_successful() pytest.mark.parametrize(email,password,expected_error, [ (, password123, 邮箱不能为空), (invalid-email, password123, 邮箱格式不正确), (testexample.com, , 密码不能为空), (wrongexample.com, wrongpass, 用户名或密码错误) ]) def test_invalid_login(self, email, password, expected_error): 测试无效登录 self.login_page.login(email, password) error_message self.login_page.get_error_message() assert expected_error in error_message def test_login_form_elements(self): 测试登录表单元素存在性 assert self.login_page.is_element_visible(self.login_page.EMAIL_INPUT) assert self.login_page.is_element_visible(self.login_page.PASSWORD_INPUT) assert self.login_page.is_element_visible(self.login_page.LOGIN_BUTTON)商品搜索测试# pages/search_page.py from selenium.webdriver.common.by import By from pages.base_page import BasePage class SearchPage(BasePage): 搜索页面 SEARCH_INPUT (By.NAME, search) SEARCH_BUTTON (By.CLASS_NAME, search-btn) SEARCH_RESULTS (By.CLASS_NAME, product-item) NO_RESULTS_MESSAGE (By.CLASS_NAME, no-results) PRODUCT_TITLE (By.CLASS_NAME, product-title) def search_product(self, keyword): 搜索商品 self.input_text(self.SEARCH_INPUT, keyword) self.click(self.SEARCH_BUTTON) def get_search_results_count(self): 获取搜索结果数量 results self.driver.find_elements(*self.SEARCH_RESULTS) return len(results) def get_first_product_title(self): 获取第一个商品标题 return self.get_text(self.PRODUCT_TITLE) # tests/test_search.py import pytest from pages.search_page import SearchPage from utils.config import Config class TestSearch: 搜索功能测试类 pytest.fixture(autouseTrue) def setup(self, driver): self.driver driver self.search_page SearchPage(driver) self.search_page.open(Config.BASE_URL) def test_valid_search(self): 测试有效搜索 self.search_page.search_product(iPhone) assert self.search_page.get_search_results_count() 0 def test_search_no_results(self): 测试无结果搜索 self.search_page.search_product(不存在的商品) assert self.search_page.is_element_visible(self.search_page.NO_RESULTS_MESSAGE) pytest.mark.parametrize(keyword, [ iPhone, Samsung, 小米, 华为 ]) def test_multiple_searches(self, keyword): 测试多个关键词搜索 self.search_page.search_product(keyword) results_count self.search_page.get_search_results_count() assert results_count 0 # 至少返回0个结果页面对象模式(POM)页面对象模式是自动化测试中的重要设计模式它将页面元素和操作封装在独立的类中。完整的POM实现# pages/product_page.py from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import Select from pages.base_page import BasePage class ProductPage(BasePage): 商品详情页 # 商品信息元素 PRODUCT_TITLE (By.H1, product-title) PRODUCT_PRICE (By.CLASS_NAME, price) PRODUCT_DESCRIPTION (By.CLASS_NAME, description) # 购买相关元素 QUANTITY_SELECT (By.NAME, quantity) ADD_TO_CART_BUTTON (By.ID, add-to-cart) CART_SUCCESS_MESSAGE (By.CLASS_NAME, cart-success) # 评论相关元素 REVIEWS_SECTION (By.ID, reviews) REVIEW_INPUT (By.NAME, review) SUBMIT_REVIEW_BUTTON (By.ID, submit-review) def get_product_info(self): 获取商品信息 return { title: self.get_text(self.PRODUCT_TITLE), price: self.get_text(self.PRODUCT_PRICE), description: self.get_text(self.PRODUCT_DESCRIPTION) } def add_to_cart(self, quantity1): 添加到购物车 # 选择数量 quantity_select Select(self.find_element(self.QUANTITY_SELECT)) quantity_select.select_by_value(str(quantity)) # 点击添加到购物车 self.click(self.ADD_TO_CART_BUTTON) # 等待成功消息 return self.is_element_visible(self.CART_SUCCESS_MESSAGE) def submit_review(self, review_text): 提交评论 self.input_text(self.REVIEW_INPUT, review_text) self.click(self.SUBMIT_REVIEW_BUTTON)购物车页面测试# tests/test_cart.py import pytest from pages.product_page import ProductPage from pages.cart_page import CartPage from utils.config import Config class TestShoppingCart: 购物车功能测试 pytest.fixture(autouseTrue) def setup(self, driver): self.driver driver self.product_page ProductPage(driver) self.cart_page CartPage(driver) def test_add_single_product_to_cart(self): 测试添加单个商品到购物车 # 打开商品页面 self.product_page.open(f{Config.BASE_URL}/product/1) # 添加到购物车 success self.product_page.add_to_cart(quantity1) assert success # 验证购物车 self.cart_page.open(f{Config.BASE_URL}/cart) assert self.cart_page.get_cart_items_count() 1 def test_add_multiple_quantities(self): 测试添加多数量商品 self.product_page.open(f{Config.BASE_URL}/product/1) success self.product_page.add_to_cart(quantity3) assert success self.cart_page.open(f{Config.BASE_URL}/cart) total_quantity self.cart_page.get_total_quantity() assert total_quantity 3 def test_cart_total_calculation(self): 测试购物车总价计算 # 添加多个商品 products [ {id: 1, quantity: 2, price: 99.99}, {id: 2, quantity: 1, price: 149.99} ] expected_total 0 for product in products: self.product_page.open(f{Config.BASE_URL}/product/{product[id]}) self.product_page.add_to_cart(product[quantity]) expected_total product[price] * product[quantity] self.cart_page.open(f{Config.BASE_URL}/cart) actual_total self.cart_page.get_total_price() assert actual_total expected_total测试报告生成HTML报告配置# pytest.ini [tool:pytest] minversion 6.0 addopts -v --strict-markers --htmlreports/report.html --self-contained-html testpaths tests python_files test_*.py python_classes Test* python_functions test_* markers smoke: 冒烟测试 regression: 回归测试 slow: 慢速测试Allure报告集成# conftest.py 添加allure配置 import allure import pytest from selenium import webdriver pytest.hookimpl(tryfirstTrue, hookwrapperTrue) def pytest_runtest_makereport(item, call): 生成测试报告钩子 outcome yield report outcome.get_result() if report.when call and report.failed: # 测试失败时自动截图 driver item.funcargs.get(driver) if driver: allure.attach( driver.get_screenshot_as_png(), name失败截图, attachment_typeallure.attachment_type.PNG ) # 在测试中使用allure装饰器 import allure class TestLoginWithAllure: 带Allure报告的登录测试 allure.epic(用户管理) allure.feature(用户登录) allure.story(正常登录流程) allure.severity(allure.severity_level.CRITICAL) def test_valid_login_with_allure(self, driver): 测试有效登录 - Allure版本 with allure.step(打开登录页面): login_page LoginPage(driver) login_page.open(f{Config.BASE_URL}/login) with allure.step(输入登录凭证): login_page.login( Config.TEST_USER[email], Config.TEST_USER[password] ) with allure.step(验证登录结果): assert login_page.is_login_successful() allure.attach( driver.get_screenshot_as_png(), name登录成功截图, attachment_typeallure.attachment_type.PNG )持续集成配置GitHub Actions配置# .github/workflows/test.yml name: 自动化测试 on: push: branches: [ main, develop ] pull_request: branches: [ main ] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - name: 设置Python环境 uses: actions/setup-pythonv3 with: python-version: 3.9 - name: 安装Chrome uses: browser-actions/setup-chromelatest - name: 安装依赖 run: | python -m pip install --upgrade pip pip install -r requirements.txt - name: 运行测试 run: | pytest tests/ --htmlreports/report.html --alluredirallure-results - name: 生成Allure报告 uses: simple-elf/allure-report-actionmaster if: always() with: allure_results: allure-results allure_history: allure-history - name: 上传测试报告 uses: actions/upload-artifactv3 if: always() with: name: test-reports path: | reports/ allure-report/Docker配置# Dockerfile FROM python:3.9-slim # 安装系统依赖 RUN apt-get update apt-get install -y \ wget \ gnupg \ unzip \ curl # 安装Chrome RUN wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add - \ echo deb [archamd64] http://dl.google.com/linux/chrome/deb/ stable main /etc/apt/sources.list.d/google-chrome.list \ apt-get update \ apt-get install -y google-chrome-stable # 设置工作目录 WORKDIR /app # 复制项目文件 COPY requirements.txt . RUN pip install -r requirements.txt COPY . . # 运行测试 CMD [pytest, tests/, --htmlreports/report.html]最佳实践和进阶技巧测试数据管理# utils/test_data.py import json import yaml from pathlib import Path class TestDataManager: 测试数据管理器 def __init__(self, data_dirtest_data): self.data_dir Path(data_dir) def load_json_data(self, filename): 加载JSON测试数据 file_path self.data_dir / f{filename}.json with open(file_path, r, encodingutf-8) as f: return json.load(f) def load_yaml_data(self, filename): 加载YAML测试数据 file_path self.data_dir / f{filename}.yaml with open(file_path, r, encodingutf-8) as f: return yaml.safe_load(f) # test_data/login_data.yaml valid_users: - email: user1example.com password: password123 expected_result: success - email: user2example.com password: password456 expected_result: success invalid_users: - email: password: password123 expected_error: 邮箱不能为空 - email: invalid-email password: password123 expected_error: 邮箱格式不正确失败重试机制# conftest.py import pytest pytest.fixture(autouseTrue) def retry_failed_tests(request): 失败测试重试机制 if request.node.rep_call.failed: # 重试逻辑 pass # 使用pytest-rerunfailures插件 # pip install pytest-rerunfailures # pytest --reruns 3 --reruns-delay 2并行测试执行# 安装pytest-xdist # pip install pytest-xdist # 并行执行测试 # pytest -n auto # 自动检测CPU核心数 # pytest -n 4 # 使用4个进程测试环境管理# utils/environment.py import os from enum import Enum class Environment(Enum): DEV dev TEST test STAGING staging PROD prod class EnvironmentConfig: 环境配置管理 def __init__(self): self.current_env Environment(os.getenv(TEST_ENV, test)) def get_base_url(self): 获取当前环境的基础URL urls { Environment.DEV: http://dev.example.com, Environment.TEST: http://test.example.com, Environment.STAGING: http://staging.example.com, Environment.PROD: http://example.com } return urls[self.current_env] def get_database_config(self): 获取数据库配置 configs { Environment.TEST: { host: test-db.example.com, database: test_db }, Environment.STAGING: { host: staging-db.example.com, database: staging_db } } return configs.get(self.current_env, {})性能测试集成# tests/test_performance.py import time import pytest from selenium.webdriver.support.ui import WebDriverWait class TestPerformance: 性能测试 def test_page_load_time(self, driver): 测试页面加载时间 start_time time.time() driver.get(https://example.com) WebDriverWait(driver, 10).until( lambda d: d.execute_script(return document.readyState) complete ) load_time time.time() - start_time assert load_time 5.0, f页面加载时间过长: {load_time}秒 def test_search_response_time(self, driver, search_page): 测试搜索响应时间 search_page.open(https://example.com) start_time time.time() search_page.search_product(iPhone) # 等待搜索结果出现 WebDriverWait(driver, 10).until( lambda d: len(d.find_elements(*search_page.SEARCH_RESULTS)) 0 ) response_time time.time() - start_time assert response_time 3.0, f搜索响应时间过长: {response_time}秒数据库验证# utils/database.py import sqlite3 import pymongo from contextlib import contextmanager class DatabaseHelper: 数据库操作助手 def __init__(self, db_config): self.config db_config contextmanager def get_connection(self): 获取数据库连接 if self.config[type] sqlite: conn sqlite3.connect(self.config[path]) elif self.config[type] mysql: import mysql.connector conn mysql.connector.connect(**self.config) try: yield conn finally: conn.close() def verify_user_created(self, email): 验证用户是否创建成功 with self.get_connection() as conn: cursor conn.cursor() cursor.execute(SELECT * FROM users WHERE email ?, (email,)) result cursor.fetchone() return result is not None # 在测试中使用数据库验证 def test_user_registration_with_db_verification(driver, db_helper): 测试用户注册并验证数据库 # 执行注册操作 registration_page RegistrationPage(driver) test_email ftest_{int(time.time())}example.com registration_page.register_user( emailtest_email, passwordpassword123 ) # 验证UI显示成功 assert registration_page.is_registration_successful() # 验证数据库中确实创建了用户 assert db_helper.verify_user_created(test_email)总结本教程全面介绍了使用pytest和selenium进行Python自动化测试的完整流程从环境搭建到高级技巧涵盖了实际项目中的各个方面。关键要点回顾环境搭建: 正确配置Python环境、浏览器驱动和项目结构pytest框架: 掌握基础测试、fixture机制和参数化测试selenium操作: 学会元素定位、常用操作和等待机制页面对象模式: 使用POM提高代码复用性和维护性测试报告: 生成专业的HTML和Allure测试报告持续集成: 配置CI/CD流程实现自动化测试最佳实践: 应用进阶技巧提升测试质量和效率测试代码https://download.csdn.net/download/exlink2012/93366322
返回列表