Metadata-Version: 2.4
Name: pytest-testcase-collector
Version: 1.0.0
Summary: Pytest plugin for collecting test metadata and generating XML report
Author: Huawei
Author-email: 
License: MIT License
        
        Copyright (c) 2026 Huawei
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Keywords: pytest,pytest-plugin,metadata,xml-report,test-collector
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Pytest
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Testing
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pytest>=6.0
Provides-Extra: dev
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

# pytest-testcase-collector

一个 pytest 插件，可自动收集测试用例报告数据（名称、级别、类型、所属类/模块、代码仓等），并生成指定格式的 XML 文件。

## 核心功能

1. **元数据采集**：提取用例名称、级别（`@pytest.mark.case_info(level)`）、类型（`@pytest.mark.case_info(type)`）、所属类/模块信息，支持参数化用例
2. **Git 信息集成**：自动识别 Git 仓库，获取仓库 URL、分支信息（非 Git 仓库自动兼容）
3. **XML 生成**：按固定结构生成包含流水线/任务标识的 XML 元数据文件
4. **Flaky 重试支持**：支持标记不稳定测试用例进行自动重试，满足最小通过次数要求
5. **环境变量控制**：支持通过环境变量控制 JUnit XML 输出和元数据收集行为
6. **灵活配置**：支持命令行指定输出路径、流水线 ID 等参数，提供完善的错误处理

## 快速开始

### 1. 安装插件

#### 方式 1：从 whl 包安装（推荐）

```bash
# 先构建 whl 包（需提前安装 build 模块）
pip install build
python -m build --wheel

# 安装生成的whl包
pip install dist/pytest_report_uploader-1.0.0-py3-none-any.whl
```

#### 方式 2：源码开发模式安装

```bash
pip install -e .
```

### 2. 使用示例

#### 仅收集用例 + 生成元数据（推荐）

```bash
pytest test_first.py --pipeline-id 123456 --pipeline-run-id 789012 --job-id 345678 --collect-only
```

#### 运行测试 + 生成元数据

```bash
pytest test_first.py --pipeline-id 123456 --pipeline-run-id 789012 --job-id 345678
```

#### 使用环境变量控制行为

```bash
# 启用自动 JUnit XML 输出
export ENABLE_JUNITXML_OUTPUT=1
export RESULTS_XML_DIR=./test-results
pytest test_first.py

# 禁用元数据收集
export DISABLE_METADATA_COLLECTION=1
pytest test_first.py
```

### 3. 命令行参数说明

所有参数均为可选，未指定则对应字段留空

| 参数 | 说明 | 默认值 |
|------|------|--------|
| `--pipeline-id` | 流水线唯一标识 | 无 |
| `--pipeline-run-id` | 流水线运行记录 ID | 无 |
| `--job-id` | 任务/作业唯一标识 | 无 |
| `--metadata-output` | 元数据 XML 输出文件路径 | `metadata.xml` |

### 4. 环境变量说明

| 环境变量 | 说明 | 默认值 |
|---------|------|--------|
| `ENABLE_JUNITXML_OUTPUT` | 设置为 `1`/`true`/`yes` 时自动添加 `--junitxml` 参数 | 不启用 |
| `RESULTS_XML_DIR` | JUnit XML 输出目录 | `results_xml_dir` |
| `DISABLE_METADATA_COLLECTION` | 设置为 `1`/`true`/`yes` 时禁用元数据收集和 XML 生成 | 启用 |

## Flaky 测试重试

### 使用方式

```python
import pytest


@pytest.mark.flaky(reruns=3, reruns_delay=2, min_pass=2)
def test_flaky_test():
    """不稳定的测试用例，最多重试3次，每次间隔2秒，至少通过2次"""
    import random

    assert random.choice([True, False, True])
```

### 参数说明

| 参数 | 说明 | 默认值 |
|------|------|--------|
| `reruns` | 重试次数 | 1 |
| `reruns_delay` | 重试间隔（秒） | 0 |
| `min_pass` | 最小通过次数（需同时设置 reruns > 0） | 1 |

**注意**：只有同时设置 `reruns > 0` 和 `min_pass > 0` 时，才会启用自定义重试逻辑。

## 示例展示

### 测试用例示例

```python
import pytest


@pytest.mark.case_info(level="L0", type="Functional")
def test_first():
    assert 1 == 1


@pytest.mark.case_info(level="L1", type="Functional")
@pytest.mark.parametrize("a,b", [("3+5", 8), ("3+2", 5)])
def test_second(a, b):
    assert eval(a) == b


@pytest.mark.flaky(reruns=3, min_pass=2)
def test_flaky_example():
    import random

    assert random.choice([True, False])
```

### 生成的 XML 示例

```xml
<?xml version='1.0' encoding='utf-8'?>
<metadata>
    <pipelineId>123456</pipelineId>
    <pipelineRunId>789012</pipelineRunId>
    <jobId>345678</jobId>
    <frameType>pytest</frameType>
    <repoUrl>https://gitcode.com/openlibing/openlibing-pytest-executor.git</repoUrl>
    <repoBranch>main</repoBranch>
    <testCases>
        <testCase>
            <name>test_first</name>
            <level>L0</level>
            <type>Functional</type>
            <className>test_first</className>
            <filePath>test_first.py</filePath>
        </testCase>
        <testCase>
            <name>test_second[3+5-8]</name>
            <level>L1</level>
            <type>Functional</type>
            <className>test_first</className>
            <filePath>test_first.py</filePath>
        </testCase>
        <testCase>
            <name>test_second[3+2-5]</name>
            <level>L1</level>
            <type>Functional</type>
            <className>test_first</className>
            <filePath>test_first.py</filePath>
        </testCase>
    </testCases>
</metadata>
```

## 技术架构

### 整体流程

```
命令行参数处理 → 测试用例收集 → 元数据提取 → Git 仓库信息获取 → XML 文件生成 → 测试执行（支持 flaky 重试）→ JUnit XML 修改
```

### 项目结构

```
pytest-testcase-collector/
├── pytest_testcase_collector/     # 插件主包
│   ├── __init__.py                # 包初始化
│   ├── conftest.py                # pytest 钩子实现（核心）
│   ├── metadata_collector.py      # 元数据收集核心逻辑
│   ├── flaky_rerun.py             # flaky 用例重试处理
│   ├── git_info.py                # Git 信息获取
│   ├── xml_generator.py           # XML 生成
│   └── error_handler.py           # 错误处理
├── test_first.py                  # 测试示例
├── readMe.md                      # 说明文档
├── setup.py                       # 安装配置
├── pyproject.toml                 # 依赖与元数据管理
└── pytest.ini                     # pytest 配置
```

### 核心钩子函数

| 函数 | 作用 | 执行时机 |
|------|------|----------|
| `pytest_load_initial_conftests` | 根据环境变量添加 `--junitxml` 参数 | 命令行参数解析后，插件初始化前 |
| `pytest_sessionstart` | 初始化元数据存储 | 测试会话开始时 |
| `pytest_collection_modifyitems` | 收集每个测试用例的元数据 | 测试用例收集完成后 |
| `pytest_collection_finish` | 生成元数据 XML 文件 | 测试用例收集完成后 |
| `pytest_runtest_protocol` | 处理 flaky 测试重试逻辑 | 每个测试用例执行前 |
| `pytest_sessionfinish` | 修改 JUnit XML 添加 flaky 标识 | 测试会话结束时 |

## 错误处理

插件内置完善的异常处理机制：

- **元数据提取失败**：输出明确的错误提示，不中断整体流程
- **XML 生成失败**：记录详细日志并提示可能的原因（如路径无写入权限）
- **Git 信息获取失败**：自动跳过并将 `repoUrl`/`repoBranch` 置空，兼容非 Git 环境
- **Flaky 重试失败**：记录重试次数和结果，不影响其他测试执行

## 注意事项

1. 使用 `--collect-only` 参数时，仅收集用例元数据，不执行测试
2. 元数据收集默认启用，可通过 `DISABLE_METADATA_COLLECTION=1` 禁用
3. JUnit XML 输出默认关闭，可通过 `ENABLE_JUNITXML_OUTPUT=1` 启用
4. Flaky 重试需要同时设置 `reruns > 0` 和 `min_pass > 0` 才会生效
