Pytest+Selenium4 Web自动化测试框架(三日速通)

发布于:2025-09-01 ⋅ 阅读:(27) ⋅ 点赞:(0)

一、Web自动化测试基础认知

1. 测试岗位体系与自动化价值

  • 岗位层级:测试工程师(初/中/高)→ 测试组长(初/资深)→ 测试经理(初/资深)→ 交付部/事业部经理(战略决策)
  • Web自动化ROI(投入产出比)(手工执行成本×迭代次数) - 首次自动化成本 - (脚本维护×维护次数)
    类比示例:功能测试=徒步找工作,Web自动化=先造车再开车找工作(长期迭代场景下自动化更高效)

2. Web自动化测试四个层次

层次 能力描述 核心目标
小白阶段 掌握基础Python、元素定位、Selenium基础操作 实现简单线性脚本
轻量级POM封装 封装页面对象,调用PO属性/方法执行用例 解决代码复用与维护问题
关键字KDT封装 封装通用关键字,通过调用关键字执行用例 降低用例编写门槛
零代码极限封装 仅需编写Excel用例即可执行自动化 非技术人员可参与用例设计

二、环境搭建与基础脚本

1. 版本与安装

工具/框架 版本要求 安装命令/操作
Selenium 4.25(最新) pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple/
Python 3.11 安装时勾选“添加环境变量到Path”
Pycharm 最新 默认安装即可
谷歌浏览器 最新 默认安装
浏览器驱动 与浏览器版本匹配 复制到项目根目录

2. 基础脚本与关键配置

  • 最小化脚本:启动浏览器、访问页面、停留后关闭
    import time
    from selenium import webdriver
    driver = webdriver.Chrome()  # 创建浏览器对象
    driver.get("https://www.baidu.com")  # 访问页面
    time.sleep(5)  # 停留5秒
    driver.quit()  # 关闭浏览器
    
  • 浏览器不自动关闭:通过ChromeOptions配置
    options = webdriver.ChromeOptions()
    options.add_experimental_option("detach", True)  # 禁止自动关闭
    driver = webdriver.Chrome(options=options)
    

三、元素定位与实战操作

1. 定位核心逻辑

  • 前提:元素属性唯一(确保定位准确性)
  • Selenium八大定位方式
    ID(底层转CSS)、NAME(底层转CSS)、LINK_TEXT、PARTIAL_LINK_TEXT、XPATH、CSS_SELECTOR、TAG_NAME(几乎不用)、CLASS_NAME(底层转CSS)
  • 推荐定位方式:优先用XPATH(覆盖所有场景,语法灵活),其次用CSS_SELECTOR(语法复杂、不支持文本定位)

2. XPATH定位详解

定位类型 语法示例 说明
绝对路径 /html/body/form/table/tbody/tr/td[2]/input 从根标签到目标标签,维护性差
相对路径+索引 //input[2] 定位页面中第2个input标签
相对路径+属性 单属性://input[@name="username"];多属性://input[@type="submit" and @value="进入管理中心"] 通过标签属性定位,最常用
相对路径+部分属性 //input[starts-with(@value,"进入")](开头匹配);//input[contains(@value,"管理")](包含匹配) 适用于属性值动态变化场景
相对路径+文本 //a[text()="商品列表"] 匹配标签中间的完整文本(替代LINK_TEXT)
相对路径+通配符 //*[text()="商品列表"]//*[@type="submit"] *匹配任意标签

3. 特殊场景处理

  • 框架(frame/iframe):元素在框架内时需先切换
    driver.switch_to.frame("menu-frame")  # 进入框架(传frame的id/name)
    driver.switch_to.default_content()    # 退出框架
    
  • 下拉框(select):用Select类操作
    from selenium.webdriver.support.select import Select
    ele = driver.find_element(By.XPATH, "//select[@name='cat_id']")
    sel = Select(ele)
    sel.select_by_index(2)          # 按索引(从0开始)
    sel.select_by_value("2")         # 按option的value属性
    sel.select_by_visible_text("手机类型")  # 按option的可见文本
    
  • 只读日历:通过JS移除readonly属性后操作
    ele = driver.find_element(By.XPATH, "//input[@name='promote_end_date']")
    driver.execute_script("arguments[0].removeAttribute('readonly');", ele)  # 移除只读
    ele.clear()
    ele.send_keys("2025-05-05")
    
  • 文件上传:直接用send_keys传文件路径(仅适用于input[type="file"]
    driver.find_element(By.XPATH, "//input[@name='goods_img']").send_keys(r"D:\aaa.png")
    
  • 定位一组元素:用find_elements获取列表,按索引操作
    ele_list = driver.find_elements(By.XPATH, "//img[@src='images/icon_trash.gif']")
    ele_list[0].click()  # 点击第一个元素
    
  • Alert弹窗:切换到弹窗后操作
    ale = driver.switch_to.alert
    ale.accept()  # 确认弹窗
    # ale.dismiss()  # 取消弹窗
    

四、测试框架核心(Pytest+Fixture+POM)

1. Pytest用例管理

  • 用例发现规则
    模块名以test_开头、类名以Test开头、方法名以test_开头
  • 运行方式
    1. 命令行:pytest(直接执行)
    2. 主函数:
      import pytest
      if __name__ == '__main__':
          pytest.main()
      
  • 全局配置文件(pytest.ini):放在项目根目录,自定义执行规则
    [pytest]
    addopts = -vs -m smoke  # 命令行参数(-vs显示详细日志,-m执行标记用例)
    testpaths = ./testcases  # 用例文件夹
    python_files = test_*.py  # 用例模块名规则
    python_classes = Test*    # 用例类名规则
    python_functions = test_* # 用例方法名规则
    markers = smoke:冒烟测试  # 用例标记定义
    
  • 核心插件:通过requirements.txt批量安装(如pytest-allure-adaptorpandas等)

2. Conftest+Fixture实现前后置

  • Conftest.py
    • 固定文件名,仅用于存放Fixture,无需导包即可调用
    • 通常放在testcases目录下
  • Fixture(固件/夹具):定义前后置操作,通过scope控制作用域
    import pytest
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    
    # scope="function"(默认,每个方法前后),autouse=False(需手动调用)
    @pytest.fixture(scope="function", autouse=False)
    def all_case_fixture():
        print("前置:打开浏览器并登录")
        driver = login_ecshop()  # 自定义登录函数
        yield driver  # 分割前置与后置(yield后为后置)
        print("后置:关闭浏览器")
    
    # 登录函数(示例:登录ECShop后台)
    def login_ecshop():
        options = webdriver.ChromeOptions()
        options.add_experimental_option("detach", True)
        driver = webdriver.Chrome(options=options)
        driver.get("http://192.168.0.44/ecshop/admin/privilege.php?act=logout")
        driver.find_element(By.XPATH, "//input[@name='username']").send_keys("admin")
        driver.find_element(By.XPATH, "//input[@name='password']").send_keys("admin123")
        driver.find_element(By.XPATH, "//input[@value='进入管理中心']").click()
        return driver
    
  • Fixture作用域function(方法)> class(类)> module(模块)> session(项目)

3. POM(Page Object Model)设计模式

  • 核心思想:将页面封装为“页面对象类”,用例层调用类的属性/方法,实现“数据-代码-页面”分离
  • 三层结构
    层级 职责 示例代码片段
    基础页面层(BasePage) 重写Selenium基础方法(如find_element、click),封装全局通用方法(如切换框架、截图) ```python
    class BasePage:
      def __init__(self, driver):
          self.driver = driver
      def find_ele(self, locator):
          return self.driver.find_element(*locator)
      def click_ele(self, locator):
          self.find_ele(locator).click()
    
    | 页面对象层(PageObject) | 继承BasePage,定义当前页面的元素定位(属性)和业务操作(方法) | 
    ```python
    class LoginPage(BasePage):
        # 元素定位(元组格式:(定位方式, 定位表达式))
        username_loc = (By.XPATH, "//input[@name='username']")
        password_loc = (By.XPATH, "//input[@name='password']")
        login_btn_loc = (By.XPATH, "//input[@value='进入管理中心']")
        
        # 业务方法:登录操作
        def login(self, username, password):
            self.find_ele(self.username_loc).send_keys(username)
            self.find_ele(self.password_loc).send_keys(password)
            self.click_ele(self.login_btn_loc)
    ```|
    | 测试用例层 | 创建页面对象实例,调用方法执行用例,传入测试数据 | ```python
    class TestLogin:
        def test_login_success(self, all_case_fixture):
            login_page = LoginPage(all_case_fixture)
            login_page.login("admin", "admin123")  # 调用登录方法
    ```|
    
  • POM优势:降低代码冗余、提升维护效率(页面变更仅需修改页面对象类)

五、数据驱动与测试报告

1. 数据驱动(Parametrize+Excel)

  • 核心工具@pytest.mark.parametrize(Pytest内置)+ pandas(读取Excel)

  • 步骤1:准备Excel测试数据
    示例表格(商品查询用例):

    index case_name type brand top supplier up search
    1 根据分类查询商品 0 0 0 0 0 拜林
    2 根据分类和品牌组合查询 5 1 0 0 0 翻1
  • 步骤2:安装依赖库

    pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple/
    pip install openpyxl -i https://pypi.tuna.tsinghua.edu.cn/simple/  # 读取Excel需此库
    
  • 步骤3:读取Excel数据为字典列表

    import pandas
    
    def read_excel_to_list(excel_path, sheet_name):
        # 读取Excel(engine='openpyxl'支持.xlsx格式)
        data = pandas.read_excel(excel_path, sheet_name=sheet_name, engine='openpyxl')
        # 转换为字典列表(每条用例为一个字典)
        dict_list = data.to_dict(orient='records')
        return dict_list
    
    # 测试:读取商品查询用例
    if __name__ == '__main__':
        cases = read_excel_to_list(r"./testcases/sousuo_shop.xlsx", "select_shop")
        for case in cases:
            print(case)  # 输出:{'index':1, 'case_name':'根据分类查询商品', ...}
    
  • 步骤4:结合Pytest实现数据驱动

    import pytest
    from pageobject.shop_page import ShopList  # 导入页面对象
    
    class TestShop:
        # 数据驱动:传入Excel读取的用例列表
        @pytest.mark.parametrize("caseinfo", read_excel_to_list(r"./testcases/sousuo_shop.xlsx", "select_shop"))
        def test_select_shop(self, all_case_fixture, caseinfo):
            sl = ShopList(all_case_fixture)  # 创建页面对象
            sl.switch_to_frame()  # 切换框架(页面方法)
            sl.select_by_value(str(caseinfo["type"]))  # 按分类查询(传入Excel数据)
            sl.select_brand_by_value(str(caseinfo["brand"]))  # 按品牌查询
            sl.click_search()  # 点击搜索
    

2. Pytest+Allure生成美观报告

  • 步骤1:Allure安装与配置

    1. 下载:https://pan.baidu.com/s/1pk3veMjY16YWpBvORwKGSg?pwd=snc6
    2. 解压:路径无中文(如D:\allure-2.21.0
    3. 配置环境变量:将D:\allure-2.21.0\bin加入系统PATH
    4. 验证:命令行输入allure --version(重启Pycharm生效)
  • 步骤2:生成临时JSON报告
    修改pytest.iniaddopts

    addopts = -vs --alluredir ./temps --clean-alluredir  # ./temps存放临时报告,--clean清空旧报告
    
  • 步骤3:生成HTML报告
    在主函数中添加生成命令:

    import pytest
    import os
    import time
    
    if __name__ == '__main__':
        pytest.main()
        time.sleep(3)  # 等待临时报告生成
        # 生成HTML报告到./reports目录,--clean清空旧报告
        os.system("allure generate ./temps -o ./reports --clean")
    
  • 步骤4:报告定制(添加用例信息)

    import allure
    
    # 项目级描述
    @allure.epic("项目:Pytest+Selenium4 Web自动化测试框架训练营")
    class TestShop:
        @pytest.mark.parametrize("caseinfo", read_excel_to_list(r"./testcases/sousuo_shop.xlsx", "select_shop"))
        def test_select_shop(self, all_case_fixture, caseinfo):
            # 定制报告层级:模块→页面→用例名称
            allure.dynamic.feature("模块:商品管理")
            allure.dynamic.story("页面:商品列表")
            allure.dynamic.title(f"用例:{caseinfo['case_name']}")  # 用Excel的用例名
            
            # 用例步骤(略,即页面对象方法调用)
            sl = ShopList(all_case_fixture)
            sl.switch_to_frame()
            sl.select_by_value(str(caseinfo["type"]))
    

六、企业级Web自动化落地技能栈

1. 核心技术栈

类别 技术/工具 说明
编程语言 Python/Java 主流自动化开发语言(Python更简洁,适合快速落地)
设计模式 POM/关键字驱动/零代码封装 POM用于中大型项目;关键字驱动降低门槛;零代码支持Excel用例
用例管理 Pytest/unittest Pytest功能更强(支持参数化、插件、灵活配置)
分布式执行 Selenium Grid 多浏览器/多节点并行执行,提升用例执行效率
数据驱动 Pytest Parametrize/DDT Parametrize内置轻量;DDT支持Excel/CSV/YAML
二次封装 Selenium基础方法、数据库(MySQL)、配置文件 封装常用操作(如元素等待、截图),支持数据持久化
日志与异常 日志模块(logging)、异常捕获(try-except) 记录用例执行过程,定位失败原因
持续集成 Jenkins 实现“代码提交→自动执行→报告生成”无人值守
容器化 Docker 统一测试环境,避免环境差异导致的用例失败
报告定制 Allure 支持自定义logo、用例层级、步骤详情

2. 企业落地分工示例

  • 测试组长:框架设计、维护核心代码
  • 测试工程师1:编写Excel用例(零代码场景)
  • 测试工程师2:维护页面对象类、脚本调试
  • 持续集成:Jenkins定时执行,失败邮件通知

3. 岗位能力与薪资

  • 技能要求:Python+接口测试+Web自动化(15-30K);附加APP/性能测试(薪资上浮)
  • 小公司:侧重工具使用(如Selenium基础脚本)
  • 中公司:侧重自动化框架落地(POM、数据驱动)
  • 大公司:侧重平台化建设(分布式、零代码、多端统一框架)