pytest-executor

pytest-executor 是一个测试环境调度器,核心功能是为测试任务分配、管理和释放运行环境,并支持在多种测试框架(pytest、unittest 等)上执行测试用例。

核心功能

环境调度

  • 多环境提供商支持:支持 k8s 和 hidevlab 两种环境提供商,通过配置动态切换
  • 环境生命周期管理:自动化环境申请、状态查询、使用和释放流程
  • 并行环境分配:支持多线程并行分配多个测试环境
  • 设备资源管理:支持 GPU、CPU 等多种设备类型的资源分配

测试执行

  • 多框架支持:原生支持 pytest,可扩展支持 unittest 等其他测试框架
  • 本地/远程执行:支持在本地或远程主机上执行测试
  • 并行测试:支持多环境并行执行测试用例

结果管理

  • 日志收集:自动收集测试日志并归档
  • 结果上传:将测试结果上传到 OpenLibing 平台和 OBS 存储

项目架构

┌─────────────────────────────────────────────────────────────────┐
│                      Scheduler Runner                           │
│  (调度入口,协调整个测试流程)                                    │
└─────────────────────────────────────────────────────────────────┘
                              │
        ┌─────────────────────┼─────────────────────┐
        ▼                     ▼                     ▼
┌───────────────┐    ┌───────────────┐    ┌───────────────┐
│ EnvManager    │    │ CaseCollector │    │    Executor   │
│ (环境管理)     │    │ (用例收集)     │    │  (测试执行)    │
└───────────────┘    └───────────────┘    └───────────────┘
        │                     │                     │
        ▼                     ▼                     ▼
┌───────────────┐    ┌───────────────┐    ┌───────────────┐
│ k8s Provider  │    │ pytest        │    │ pytest        │
│ hidevlab      │    │ unittest      │    │ unittest      │
│ Provider      │    │ (可扩展)       │    │  (可扩展)      │
└───────────────┘    └───────────────┘    └───────────────┘

环境调度流程

┌──────────────┐     ┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│ 1. 申请环境   │ --> │ 2. 查询状态   │ --> │ 3. 执行测试   │ --> │ 4. 释放环境   │
│ allocate()   │     │ check_status()│     │ execute()    │     │ release()    │
└──────────────┘     └──────────────┘     └──────────────┘     └──────────────┘
        │                     │                     │                     │
        ▼                     ▼                     ▼                     ▼
   HTTP API              HTTP API              SSH/本地              HTTP API
   (k8s/hidevlab)        (k8s/hidevlab)        执行                 (k8s/hidevlab)

项目文件结构

pytest-executor/
├── src/
│   ├── api/                          # API 交互模块
│   │   ├── __init__.py
│   │   ├── api_handler.py            # OpenLibing API 处理(环境申请、状态查询、释放)
│   │   └── obs_client.py             # OBS 存储客户端
│   ├── case_collector/               # 测试用例收集模块
│   │   ├── __init__.py
│   │   ├── base_collector.py         # 用例收集器基类
│   │   ├── collector_factory.py      # 用例收集器工厂
│   │   └── pytest_collector.py       # pytest 用例收集器
│   ├── executor/                     # 测试执行模块
│   │   ├── __init__.py
│   │   ├── adapter.py                # 执行器适配器
│   │   ├── base_executor.py          # 执行器基类
│   │   ├── executor_factory.py       # 执行器工厂
│   │   └── pytest_executor.py        # pytest 执行器
│   ├── log/                          # 日志模块
│   │   ├── __init__.py
│   │   └── logger_factory.py         # 日志工厂(统一日志格式,包含线程ID)
│   ├── report/                       # 报告处理模块
│   │   ├── __init__.py
│   │   ├── log_archiver.py           # 日志归档
│   │   ├── report_uploader.py        # 报告上传
│   │   └── xml_processor.py          # XML 结果处理
│   ├── scheduler/                    # 调度器核心模块
│   │   ├── __init__.py
│   │   ├── env_manager.py            # 环境管理器(申请、查询、释放环境)
│   │   └── scheduler_config.py       # 调度器配置
│   ├── utils/                        # 工具模块
│   │   ├── __init__.py
│   │   ├── crypto_utils.py           # 加密工具
│   │   ├── data_utils.py             # 数据处理工具
│   │   └── path_utils.py             # 路径处理工具
│   ├── __init__.py
│   └── run_scheduler.py              # 调度器主入口
├── main.py                           # 命令行入口
├── requirements.txt                  # 依赖列表
├── setup.py                          # 包安装配置
├── start.sh                          # 启动脚本
└── README.md                         # 项目文档

快速开始

安装依赖

pip install -r requirements.txt

执行测试

python main.py \
    --testcase_dir /path/to/testcases \
    --log_dir /path/to/logs \
    --obs \
    --task_id test-task-001 \
    --pipeline_id pipeline-001 \
    --pipeline_run_id run-001 \
    --job_run_id job-001 \
    --scheduler_secret \
    --max_workers 4

环境提供商配置

切换环境提供商

通过 Config.env_provider 配置:

# 使用 k8s(默认)
Config.env_provider = "k8s"

# 使用 hidevlab
Config.env_provider = "hidevlab"

失败用例重跑(only_fail 特性)

功能概述

only_fail 特性允许 pytest-executor 只运行上次测试失败的用例,从而节省测试时间和资源。该特性特别适用于以下场景:

  • 增量测试:修复代码后只重新运行失败的测试用例
  • 持续集成优化:减少 CI 流水线执行时间
  • 快速验证:快速验证 bug 修复是否有效

使用方法

在测试用例目录下创建配置文件(例如 pytest.ini),添加 [only-fail] 配置段:

[only-fail]
run_only_fail_case = true

通过 --fail_case 参数指定失败测试结果 JSON 文件:

python main.py \
    --testcase_dir /path/to/testcases \
    --log_dir /path/to/logs \
    --fail_case /path/to/failed_cases.json \
    ... 其他参数

其中 failed_cases.json 文件部分内容示例如下

{
    "testCasesResult": [
        {
            "name": "test_login_failed",
            "state": "failed"
        },
        {
            "name": "test_data_error",
            "state": "error"
        },
        {
            "name": "test_success_case",
            "state": "passed"
        }
    ]
}

字段说明:

  • testCasesResult:测试用例结果数组
  • name:测试用例名称(支持格式:test_function 或 TestClass::test_method)
  • state:测试用例状态
    • failed:测试失败
    • error:测试错误
    • passed:测试通过(会被过滤掉)

过滤逻辑:

  • 只保留 state != "passed" 的用例
  • 支持多种失败状态:failed、error 等
  • 自动匹配测试用例名称(支持 :: 分隔符格式)

注意事项

  1. JSON 文件路径:--fail_case 参数指定的 JSON 文件必须存在,否则会运行所有测试用例
  2. JSON 文件格式:必须包含 testCasesResult 数组字段
  3. 用例名称匹配:支持 test_function 和 TestClass::test_method 两种格式
  4. 无失败用例:如果 JSON 文件中没有失败用例,将执行所有测试用例

相关配置项

在 src/scheduler/scheduler_config.py 中定义了两个配置项:

only_fail = False  # 是否只运行失败用例(从配置文件读取)
fail_case = ""     # 失败测试结果 JSON 文件路径(从命令行参数读取)

环境提供商实现

系统通过 EnvironmentManager 统一管理环境,自动根据配置调用对应的提供商:

class EnvironmentManager:
    def allocate_environments(self, env_case_info):
        # 根据 Config.env_provider 选择执行逻辑
        if Config.env_provider == "k8s":
            return self._query_k8s_env_status(env_case_info)
        else:
            return self._query_hidevlab_env_status(env_case_info)

    def release_env(self, task_id):
        # 根据配置选择释放逻辑
        if Config.env_provider == "k8s":
            self._call_k8s_delete_api(task_id)
        else:
            self._call_hidevlab_delete_api(task_id)

测试框架扩展机制

框架接入概述

系统通过工厂模式支持多种测试框架的接入,主要涉及两个扩展点:

扩展点 职责 接口类
CaseCollector 收集测试用例 BaseCollector
Executor 执行测试用例 BaseExecutor

接入新框架(以 unittest 为例)

步骤 1:实现 Collector

创建 src/case_collector/unittest_collector.py:

from .base_collector import BaseCollector


class UnittestCollector(BaseCollector):
    def collect(self):
        """收集 unittest 测试用例"""
        test_cases = []
        # 扫描测试目录,收集 unittest 测试用例
        # ...
        return test_cases

步骤 2:实现 Executor

创建 src/executor/unittest_executor.py:

from .base_executor import BaseExecutor


class UnittestExecutor(BaseExecutor):
    def execute(self, test_cases, env_info):
        """执行 unittest 测试用例"""
        # 执行 unittest 测试
        # ...
        return results

步骤 3:注册到工厂

修改 src/case_collector/collector_factory.py:

from .unittest_collector import UnittestCollector


class CollectorFactory:
    @staticmethod
    def create(framework_name):
        if framework_name == "pytest":
            return PytestCollector()
        elif framework_name == "unittest":
            return UnittestCollector()  # 注册新框架
        else:
            raise ValueError(f"Unsupported framework: {framework_name}")

修改 src/executor/executor_factory.py:

from .unittest_executor import UnittestExecutor


class ExecutorFactory:
    @staticmethod
    def create(framework_name):
        if framework_name == "pytest":
            return PytestExecutor()
        elif framework_name == "unittest":
            return UnittestExecutor()  # 注册新框架
        else:
            raise ValueError(f"Unsupported framework: {framework_name}")

步骤 4:配置使用

# 在 scheduler_config.py 中设置
Config.framework_name = "unittest"

扩展接口定义

BaseCollector:

class BaseCollector:
    def collect(self) -> List[TestCase]:
        """收集测试用例,返回测试用例列表"""
        raise NotImplementedError

BaseExecutor:

class BaseExecutor:
    def execute(self, test_cases: List[TestCase], env_info: dict) -> ExecutionResult:
        """执行测试用例"""
        raise NotImplementedError

核心模块说明

EnvManager(环境管理器)

负责环境的全生命周期管理:

  • allocate_environments() - 批量申请环境
  • release_env() - 释放单个环境
  • release_all() - 释放所有环境

SchedulerRunner(调度器)

协调整个测试流程:

  1. 收集测试用例
  2. 分配测试环境
  3. 执行测试用例
  4. 释放测试环境

技术栈

  • Python 3.8+
  • pytest(默认测试框架)
  • paramiko(SSH 远程执行)
  • requests(HTTP 请求)
  • esdk-obs-python(OBS 存储)

许可证

MIT License