Python + Selenium + Pytest + Allure实现UI自动化

以下是使用 Python + Selenium + Pytest + Allure 实现 UI 自动化的详细步骤指南:

1. 环境准备

1.1 安装 Python

  • 下载并安装 Python,建议使用 Python 3.8+。
  • 配置 Python 环境变量。

1.2 安装依赖库

pip install pytest selenium allure-pytest webdriver-manager
  • pytest: 测试框架
  • selenium: 浏览器自动化工具
  • allure-pytest: 生成 Allure 报告
  • webdriver-manager: 自动管理浏览器驱动

2. 项目结构

project/
├── conftest.py          # pytest 配置和共享 fixture
├── pages/               # 页面对象模型 (Page Object Model)
│   ├── __init__.py
│   └── login_page.py
├── tests/               # 测试用例
│   └── test_login.py
├── requirements.txt     # 依赖列表
└── allure-results/      # Allure 报告数据(自动生成)

3. 编写 Fixture

在 conftest.py 中配置浏览器驱动和测试失败自动截图:

import pytest
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service as ChromeService
import allure

@pytest.fixture(scope="function")
def browser(request):
    # 初始化浏览器(自动下载驱动)
    options = webdriver.ChromeOptions()
    options.add_argument("--headless")  # 无头模式(可选)
    driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()), options=options)
    
    # 最大化窗口
    driver.maximize_window()
    
    # 失败时截图
    yield driver
    if request.node.rep_call.failed:
        allure.attach(
            driver.get_screenshot_as_png(),
            name="screenshot",
            attachment_type=allure.attachment_type.PNG
        )
    driver.quit()

# Hook 函数捕获测试结果
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    outcome = yield
    rep = outcome.get_result()
    setattr(item, "rep_" + rep.when, rep)

4. 实现页面对象模型 (Page Object)

在 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)
    
    def open(self, url):
        self.driver.get(url)
    
    def enter_username(self, username):
        self.wait.until(EC.visibility_of_element_located((By.ID, "username"))).send_keys(username)
    
    def enter_password(self, password):
        self.driver.find_element(By.ID, "password").send_keys(password)
    
    def click_login(self):
        self.driver.find_element(By.XPATH, "//button[@type='submit']").click()
    
    def get_error_message(self):
        return self.wait.until(EC.visibility_of_element_located((By.CLASS_NAME, "error-message"))).text

5. 编写测试用例

在 tests/test_login.py 中编写测试逻辑:

import allure
import pytest
from pages.login_page import LoginPage

@allure.feature("登录功能")
class TestLogin:
    @allure.story("用户登录成功")
    @allure.severity(allure.severity_level.CRITICAL)
    def test_login_success(self, browser):
        login_page = LoginPage(browser)
        with allure.step("1. 打开登录页面"):
            login_page.open("https://example.com/login")
        with allure.step("2. 输入用户名和密码"):
            login_page.enter_username("valid_user")
            login_page.enter_password("valid_pass")
        with allure.step("3. 点击登录按钮"):
            login_page.click_login()
        with allure.step("4. 验证登录成功"):
            assert "Dashboard" in browser.title

    @allure.story("用户登录失败")
    @pytest.mark.parametrize("username, password, error_msg", [
        ("invalid_user", "wrong_pass", "用户名或密码错误"),
        ("", "valid_pass", "用户名不能为空")
    ])
    def test_login_failure(self, browser, username, password, error_msg):
        login_page = LoginPage(browser)
        login_page.open("https://example.com/login")
        login_page.enter_username(username)
        login_page.enter_password(password)
        login_page.click_login()
        assert error_msg in login_page.get_error_message()

6. 运行测试并生成报告

6.1 执行测试

pytest tests/test_login.py --alluredir=./allure-results

6.2 查看 Allure 报告

allure serve ./allure-results
  • 自动打开浏览器展示交互式报告。

7. 高级配置

7.1 Headless 模式

在 conftest.py 的浏览器初始化中添加无头模式选项:

options.add_argument("--headless")
options.add_argument("--disable-gpu")

7.2 参数化测试

使用 @pytest.mark.parametrize 覆盖多组数据:

@pytest.mark.parametrize("username, password", [("user1", "pass1"), ("user2", "pass2")])
def test_multiple_users(self, username, password):
    # 测试逻辑

7.3 集成到 CI/CD

在 GitHub Actions 中添加步骤:

- name: Run Tests
  run: |
    pytest tests/ --alluredir=./allure-results
- name: Generate Report
  run: |
    allure generate ./allure-results -o ./allure-report --clean

常见问题

  1. 元素定位失败使用显式等待 (WebDriverWait) 代替硬编码等待 (time.sleep).优先使用 ID 或 CSS 选择器,避免脆弱的 XPath.
  2. 浏览器驱动问题使用 webdriver-manager 自动管理驱动版本。
  3. 报告无数据确保 --alluredir 参数正确,且测试执行未跳过。

进阶高级测试工程师 文章被收录于专栏

《高级软件测试工程师》专栏旨在为测试领域的从业者提供深入的知识和实践指导,帮助大家从基础的测试技能迈向高级测试专家的行列。 在本专栏中,主要涵盖的内容: 1. 如何设计和实施高效的测试策略; 2. 掌握自动化测试、性能测试和安全测试的核心技术; 3. 深入理解测试驱动开发(TDD)和行为驱动开发(BDD)的实践方法; 4. 测试团队的管理和协作能力。 ——For.Heart

全部评论
大佬写得很好
点赞 回复 分享
发布于 03-11 11:15 上海

相关推荐

04-17 15:29
已编辑
门头沟学院 测试工程师
自我介绍+拷打项目+一点点八股(python、java、Linux操作系统)+两道场景题+手撕代码1、拷打项目为什么不用脚本实现账户密码登录而用‌Selenium模仿用户具体操作#牛客AI配图神器#Selenium自动获取网站cookie遇到http only问题你是怎么解决的request库调用是否经历过卡死pyqt中的信号槽机制,为什么一定要使用信号与槽,不使用会怎么样?为什么选择测开岗位2、八股装饰器的概念、可选参数和关键字参数什么时候用幂等是什么?为什么要设计幂等、幂等的初衷是什么?可以用什么手段来做幂等java的关键字volatile是什么linux里面怎么保存和修改文件3、场景题测试用例一个用来领取奖励的接口,奖励领取有前置条件,即需要完成一个任务后才可以领取,这个奖励每天晚上12点重置,一天只能领取一次。设计一个测试用例怎么测试接口层面,不是功能层面安全方面测试一个网页,有一个输入框、输入框内输入任意的代码,然后有一个执行的按钮,点击这个按钮后可以看到输入的代码是否执行成功。点击执行后,服务器真的会运行你提交的代码,你已经知道服务器上某个路径下面有一个文件,你需要拿到这个文件的内容,你要怎么做?4、手撕代码单向链表,升序排序、力扣148算术题给定x个数据框,每个数据框内填充n个数据,把所有数据用例测试一遍要遍历多少次?#软开人,说说你的烦心事#
查看13道真题和解析 软开人,说说你的烦心事
点赞 评论 收藏
分享
评论
点赞
8
分享

创作者周榜

更多
牛客网
牛客企业服务