如何基于 pytest 搭建一个高效、可维护、易扩展的自动化测试框架。pytest 的强大之处在于其简洁的语法和丰富的插件生态,一个好的框架设计能最大化地发挥其威力。

以下是我搭建 pytest 测试框架的核心思路和最佳实践:

一、 框架设计哲学与目标

在开始之前,明确框架的设计目标:

  1. 高可读性:测试用例应该像“散文”一样清晰,易于理解和维护。

  2. 高可维护性:业务逻辑变化时,修改成本应最低。核心是分离测试逻辑与测试数据,以及分离操作与用例。

  3. 高扩展性:能够方便地集成各种功能(如API、Web UI、App UI、数据库)并支持分布式执行。

  4. 高效执行:支持并行测试、失败重跑,快速反馈。

  5. 强大的报告:提供清晰、详细、美观的测试报告。


二、 项目目录结构(Structure)

一个清晰的结构是框架的基石。我推荐的结构如下:

bash

复制

下载

your_automation_framework/
├── conftest.py                    # 核心配置文件,定义fixture
├── pytest.ini                    # pytest配置文件,定义执行规则
├── requirements.txt              # 项目依赖包列表
├── fixtures/                     # 自定义fixture(可选,复杂时可拆分)
│   └── api_client.py
├── common/                       # 公共模块
│   ├── __init__.py
│   ├── api_client.py            # 封装的API请求类
│   ├── web_driver.py            # 封装的Web Driver类(如Selenium)
│   └── helpers.py               # 工具函数,如数据生成、文件读取
├── test_data/                    # 测试数据目录
│   ├── api/
│   │   └── test_users.json
│   └── web/
│       └── login_data.csv
├── test_cases/                   # 测试用例目录(按功能/模块划分)
│   ├── api/
│   │   ├── __init__.py
│   │   ├── conftest.py          # 模块级别的fixture
│   │   └── test_user_api.py
│   ├── web/
│   │   ├── __init__.py
│   │   ├── conftest.py
│   │   └── test_login.py
│   └── mobile/
└── reports/                      # 测试报告输出目录(.gitignore忽略)
    ├── html/
    └── xml/

解释:

  • conftest.py: 是pytest的“魔法”文件,用于定义全局共享的fixture(如初始化驱动、读取配置)。它可以存在于任何目录,作用域为该目录及其子目录。

  • pytest.ini: 用于配置pytest的默认行为(如命令行参数、标记)。

  • common/: 存放所有封装的底层操作,这是框架的核心。自动化人员在这里编写与设备、接口交互的代码。

  • test_cases/: 测试人员在这里使用common提供的API和fixture,用简洁的pytest语法编写业务测试用例。几乎不会有底层代码。

  • test_data/: 分离数据和代码,方便维护。


三、 核心组件搭建(Implementation)

1. 依赖管理 (requirements.txt)

使用pip管理项目依赖,确保环境一致。

txt

复制

下载

pytest==7.4.0
requests==2.31.0
selenium==4.12.0
pytest-html==4.1.1    # HTML报告
pytest-xdist==3.3.1   # 并行测试
pytest-rerunfailures==13.0 # 失败重试
pytest-base-url==2.0.0 # 管理基础URL
# 其他依赖:allure-pytest, pytest-ordering, pytest-bdd...

安装:pip install -r requirements.txt

2. 配置文件 (pytest.ini)

配置默认命令,让团队成员无需记忆复杂参数。

ini

复制

下载

[pytest]
# 指定默认命令行选项
addopts = -v --html=reports/html/report.html --self-contained-html -n auto
# `-n auto`: 使用xdist并行执行(CPU核心数)

# 定义标记,防止拼写错误
markers =
    smoke: marks tests as smoke tests (deselect with '-m "not smoke"')
    regression: marks tests as regression tests
    slow: slow running tests

# 设置基础URL(可通过pytest-base-url插件或自定义fixture读取)
base_url = https://api.example.com
3. 封装核心交互类 (common/)

这是体现“专家”水平的地方。将Selenium, Requests等底层操作封装成简单、健壮的方法。

示例:common/api_client.py

python

复制

下载

import requests
from urllib.parse import urljoin

class ApiClient:
    def __init__(self, base_url):
        self.base_url = base_url
        self.session = requests.Session()
        # 可以在这里统一添加headers,如auth token
        # self.session.headers.update({'Authorization': 'Bearer ...'})

    def request(self, method, endpoint, **kwargs):
        url = urljoin(self.base_url, endpoint)
        response = self.session.request(method, url, **kwargs)
        response.raise_for_status()  # 非200响应抛出异常,便于测试失败
        return response

    def get(self, endpoint, params=None, **kwargs):
        return self.request('GET', endpoint, params=params, **kwargs)

    def post(self, endpoint, json=None, data=None, **kwargs):
        return self.request('POST', endpoint, json=json, data=data, **kwargs)
    # ... 同理实现 put, delete 等方法
4. 定义Fixtures (conftest.py)

Fixture是pytest的依赖注入机制,用于管理测试生命周期(setup/teardown)和共享数据。

示例:全局conftest.py

python

复制

下载

import pytest
from common.api_client import ApiClient

# 从pytest.ini或环境变量获取基础URL
def pytest_addoption(parser):
    parser.addoption("--base-url", action="store", default="https://api.example.com", help="Base URL for the API")

# 提供一个配置好的API client fixture
@pytest.fixture(scope="session")
def api_client(pytestconfig):
    base_url = pytestconfig.getoption("--base-url")
    client = ApiClient(base_url)
    yield client
    # Teardown: 可以在这里关闭session,清理资源
    client.session.close()

# 如果是Web测试,可以定义浏览器driver的fixture
@pytest.fixture
def browser():
    from selenium import webdriver
    driver = webdriver.Chrome()
    driver.implicitly_wait(10)
    yield driver
    driver.quit()
5. 编写测试用例 (test_cases/)

现在,自动化人员可以专注于业务逻辑,而不用关心底层细节。

示例:test_cases/api/test_user_api.py

python

复制

下载

import pytest

class TestUserAPI:
    """用户API测试套件"""

    # 标记这是一个冒烟测试
    @pytest.mark.smoke
    def test_get_user_by_id(self, api_client):
        """测试通过ID获取用户信息"""
        # 1. 准备测试数据(可以从test_data/目录读取)
        user_id = 1
        # 2. 执行操作(使用封装好的api_client,非常简洁)
        response = api_client.get(f"/users/{user_id}")
        # 3. 断言响应
        assert response.status_code == 200
        user_data = response.json()
        assert user_data['id'] == user_id
        assert 'username' in user_data

    def test_create_user(self, api_client):
        """测试创建用户"""
        new_user = {"name": "test_user", "job": "tester"}
        response = api_client.post("/users", json=new_user)
        assert response.status_code == 201
        created_user = response.json()
        assert created_user['name'] == new_user['name']
        assert 'id' in created_user

四、 高效执行与报告 (Execution & Reporting)

  1. 选择性运行:

    • pytest test_cases/api/ - 运行某个模块

    • pytest -m smoke - 只运行冒烟测试

    • pytest -k "test_create" - 运行名称包含"test_create"的用例

  2. 并行与重试:

    • 通过pytest-xdist,使用-n auto即可自动并行,极大缩短执行时间。

    • 通过pytest-rerunfailures,使用--reruns 3可以将失败用例自动重试3次,应对环境抖动问题。

  3. 报告生成:

    • HTML报告:pytest --html=reports/report.html,直观清晰。

    • Allure报告:pytest --alluredir=reports/allure_results + allure serve reports/allure_results,生成非常强大、美观且可交互的报告,是展示自动化成果的利器。

五、 总结:框架的优势

通过以上设计,你的框架实现了:

  • 高效开发:自动化人员只需在test_cases目录下,使用直观的fixture和封装好的API编写业务断言。

  • 易于维护:底层代码(如HTTP请求、浏览器控制)集中在common和fixtures中,业务变化时只需修改测试数据或用例,技术变化(如库升级)时只需修改底层封装。

  • 灵活扩展:需要测试新的终端(如Mobile App),只需在common下添加新的封装类,并提供对应的fixture即可。

  • 稳定高效:通过并行、重试机制和清晰的报告,保证测试套件能快速、可靠地运行。

这个框架为自动化团队提供了一个标准化的、高效的协作平台,是高质量自动化项目的基础。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐