pytest测试框架多系统并存+yml编写测试用例实现

实现一个支持 多系统共存 的接口测试框架,同时结合 YML 文件编写测试用例,需要考虑以下关键点:

  1. 每个系统的独立性

    • 各系统可以有独立的配置(如 conftest.py、环境变量等)。
    • 测试用例按系统和模块组织。
  2. 统一管理和执行

    • 测试用例可以按系统、模块或标签(如优先级)动态选择执行。
    • 支持共享框架的核心逻辑(如用例执行、结果收集、上下文管理)。
  3. YML 用例兼容性

    • 每个系统的 YML 用例格式一致,支持用例依赖、动态参数、全局变量等。
  4. 统一测试报告和通知

    • 生成统一的测试报告,并按系统归类统计结果。
    • 支持发送钉钉、邮件等通知。

1. 目录结构设计

以下目录结构将支持多个系统的接口测试共存,并利用 YML 管理测试用例:

tests/
├── system_a/
│   ├── conftest.py          # system_a 的独立配置
│   ├── test_cases.yml       # system_a 的 YML 测试用例
│   ├── test_system_a.py     # system_a 的测试逻辑
│   ├── __init__.py
├── system_b/
│   ├── conftest.py          # system_b 的独立配置
│   ├── test_cases.yml       # system_b 的 YML 测试用例
│   ├── test_system_b.py     # system_b 的测试逻辑
│   ├── __init__.py
├── framework/
│   ├── base_test.py         # 通用测试框架逻辑
│   ├── conftest_loader.py   # 动态加载 conftest.py
│   ├── reporter.py          # 测试报告生成与通知
│   ├── utils.py             # 工具函数,如请求处理、动态参数解析
│   ├── context.py           # 上下文管理
│   ├── __init__.py
├── conftest.py              # 主 conftest.py,加载动态配置

2. YML 用例格式设计

每个系统的测试用例使用统一的 YML 格式,支持以下功能:

  1. 基础请求配置:接口地址、请求方法、头部、参数等。
  2. 用例依赖:支持用例之间的依赖,并共享动态数据。
  3. 动态参数解析:支持 $global$response 占位符。
  4. 多用例管理:多个测试用例可以写在一个 YML 文件中。

示例 YML 文件

tests/system_a/test_cases.yml
test_cases:
  - name: "创建用户"
    description: "测试 system_a 创建用户接口"
    request:
      method: POST
      url: "http://system_a.example.com/api/users"
      headers:
        Content-Type: "application/json"
      body:
        username: "user_a"
        password: "password_a"
    expected:
      status_code: 201
      body:
        id: "$response.id"
    save:
      user_id: "id"

  - name: "获取用户信息"
    depends_on: "创建用户"
    request:
      method: GET
      url: "http://system_a.example.com/api/users/$global.user_id"
      headers:
        Authorization: "Bearer $global.token"
    expected:
      status_code: 200
      body:
        username: "user_a"
tests/system_b/test_cases.yml
test_cases:
  - name: "登录"
    description: "测试 system_b 登录接口"
    request:
      method: POST
      url: "http://system_b.example.com/api/login"
      headers:
        Content-Type: "application/json"
      body:
        username: "admin"
        password: "password123"
    expected:
      status_code: 200
      body:
        token: "$response.token"
    save:
      token: "token"

  - name: "获取数据"
    depends_on: "登录"
    request:
      method: GET
      url: "http://system_b.example.com/api/data"
      headers:
        Authorization: "Bearer $global.token"
    expected:
      status_code: 200
      body:
        data: []

3. 统一测试框架实现

3.1 全局上下文管理

全局上下文用于存储动态变量(如全局 token)和测试结果,支持跨用例、跨系统共享。

framework/context.py
class Context:
    def __init__(self):
        self.global_data = {}  # 存储全局变量
        self.results = {}      # 存储测试结果

    def set_global(self, key, value):
        self.global_data[key] = value

    def get_global(self, key):
        return self.global_data.get(key)

    def add_result(self, case_name, result):
        self.results[case_name] = result

    def get_results(self):
        return self.results

3.2 用例加载器

动态加载每个系统的 YML 测试用例。

framework/utils.py
import yaml

def load_test_cases(file_path):
    """
    加载 YML 测试用例
    :param file_path: 测试用例文件路径
    :return: 测试用例列表
    """
    with open(file_path, "r", encoding="utf-8") as f:
        data = yaml.safe_load(f)
        return data["test_cases"]

3.3 用例执行器

负责处理请求、验证结果和保存动态数据。

framework/base_test.py
import requests
from framework.context import Context

class TestFramework:
    def __init__(self, context):
        self.context = context

    def execute_test_case(self, case):
        # 动态解析请求参数
        request_data = self._resolve_placeholders(case["request"])
        response = requests.request(
            method=request_data["method"],
            url=request_data["url"],
            headers=request_data.get("headers"),
            json=request_data.get("body")
        )

        # 验证响应
        self._validate_response(response, case["expected"])

        # 保存动态数据
        if "save" in case:
            for key, path in case["save"].items():
                value = self._get_nested_value(response.json(), path.split('.'))
                self.context.set_global(key, value)

        # 保存结果
        self.context.add_result(case["name"], {
            "status": "passed" if response.status_code == case["expected"]["status_code"] else "failed",
            "response": response.json()
        })

    def _resolve_placeholders(self, data):
        # 解析占位符(如 $global 和 $response)
        # 示例实现略,参考前述内容
        pass

    def _validate_response(self, response, expected):
        # 验证响应状态码和内容
        assert response.status_code == expected["status_code"]
        for key, value in expected.get("body", {}).items():
            assert response.json().get(key) == value

    def _get_nested_value(self, data, keys):
        # 根据路径获取嵌套的数据
        for key in keys:
            data = data.get(key)
        return data

3.4 动态加载 conftest.py

动态加载每个系统的 conftest.py 文件。

framework/conftest_loader.py
import os
import importlib.util
import sys

def load_conftest_files(base_dir):
    for root, _, files in os.walk(base_dir):
        if "conftest.py" in files:
            conftest_path = os.path.join(root, "conftest.py")
            module_name = os.path.basename(root) + "_conftest"
            spec = importlib.util.spec_from_file_location(module_name, conftest_path)
            module = importlib.util.module_from_spec(spec)
            sys.modules[module_name] = module
            spec.loader.exec_module(module)
conftest.py
import os
from framework.conftest_loader import load_conftest_files

base_dir = os.path.dirname(__file__)
load_conftest_files(base_dir)

3.5 pytest 集成

tests/system_a/test_system_a.py
import pytest
from framework.base_test import TestFramework
from framework.utils import load_test_cases
from framework.context import Context

context = Context()
framework = TestFramework(context)

test_cases = load_test_cases("tests/system_a/test_cases.yml")

@pytest.mark.parametrize("case", test_cases, ids=[case["name"] for case in test_cases])
def test_system_a(case):
    framework.execute_test_case(case)
tests/system_b/test_system_b.py
import pytest
from framework.base_test import TestFramework
from framework.utils import load_test_cases
from framework.context import Context

context = Context()
framework = TestFramework(context)

test_cases = load_test_cases("tests/system_b/test_cases.yml")

@pytest.mark.parametrize("case", test_cases, ids=[case["name"] for case in test_cases])
def test_system_b(case):
    framework.execute_test_case(case)

4. 测试执行

执行所有系统测试

pytest -v

仅执行 system_a 测试

pytest -k "system_a"

5. 测试报告与通知

可参考之前的 钉钉/邮件通知 实现,生成统一的测试报告并发送。


6. 总结

通过上述框架设计,实现了以下目标:

  1. 多系统共存:每个系统独立管理测试用例和配置,动态加载。
  2. YML 用例灵活性:支持用例依赖、动态参数解析和全局变量共享。
  3. 统一执行与报告:支持统一执行所有系统的测试,并生成统一的测试报告。

这种设计非常适合复杂接口测试场景,具有高扩展性和可维护性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Python测试之道

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值