首页
/ pytest项目:自定义目录收集器使用指南

pytest项目:自定义目录收集器使用指南

2025-07-06 03:43:30作者:柏廷章Berta

理解pytest默认目录收集机制

pytest框架在测试收集阶段会自动扫描项目目录结构,默认情况下会使用两种收集器处理不同类型的目录:

  1. 对于包含__init__.py文件的目录,使用pytest.Package收集器
  2. 对于普通目录,使用pytest.Dir收集器

这种默认行为适用于大多数场景,但当我们需要更精细地控制测试收集过程时,就需要自定义目录收集器。

为什么需要自定义目录收集器

在实际项目中,我们可能会遇到以下需求:

  • 需要基于特定文件(如manifest文件)控制测试收集
  • 希望实现动态测试收集逻辑
  • 需要对不同目录应用不同的收集策略
  • 需要过滤或转换收集到的测试项

这些需求都可以通过实现自定义目录收集器来满足。

实现自定义目录收集器

核心组件

  1. 自定义Directory类:继承自pytest.Directory,实现特定收集逻辑
  2. pytest_collect_directory钩子:用于注册自定义收集器

示例:基于manifest.json的收集器

下面我们通过一个完整示例来演示如何实现基于manifest文件的目录收集器。

1. 创建conftest.py

import json
import pathlib
import pytest

class ManifestDirectory(pytest.Directory):
    def collect(self):
        manifest_path = self.path / "manifest.json"
        if manifest_path.exists():
            manifest = json.loads(manifest_path.read_text())
            for file_pattern in manifest.get("files", []):
                for path in self.path.glob(file_pattern):
                    if path.name.endswith(".py") and path.is_file():
                        yield pytest.Module.from_parent(self, path=path)
        else:
            yield from super().collect()

def pytest_collect_directory(path, parent):
    if (path / "manifest.json").exists():
        return ManifestDirectory.from_parent(parent, path=path)
    return None

2. 创建manifest.json

{
    "files": [
        "test_first.py",
        "test_second.py"
    ]
}

3. 创建测试文件

# test_first.py
def test_1():
    assert True

# test_second.py
def test_2():
    assert True

# test_third.py
def test_3():
    assert True

工作原理分析

  1. 收集过程

    • pytest在收集阶段会调用pytest_collect_directory钩子
    • 当目录包含manifest.json文件时,返回自定义的ManifestDirectory收集器
    • 否则返回None,使用默认收集器
  2. ManifestDirectory收集器

    • 读取manifest.json文件
    • 根据files列表收集指定的测试文件
    • 忽略未在manifest中列出的测试文件

执行效果验证

执行测试:

$ pytest
============================ test session starts ============================
collected 2 items

tests/test_first.py .                                                [ 50%]
tests/test_second.py .                                               [100%]

============================ 2 passed in 0.12s =============================

查看收集树:

$ pytest --collect-only
============================ test session starts ============================
collected 2 items

<Dir customdirectory>
  <ManifestDirectory tests>
    <Module test_first.py>
      <Function test_1>
    <Module test_second.py>
      <Function test_2>

======================== 2 tests collected in 0.12s ========================

高级应用场景

扩展manifest功能

可以进一步扩展manifest.json的功能,例如:

  1. 测试过滤

    {
        "exclude": ["test_*_slow.py"]
    }
    
  2. 标记控制

    {
        "markers": {
            "test_*.py": "smoke"
        }
    }
    
  3. 参数化配置

    {
        "parameters": {
            "test_api.py": {"base_url": "https://api.example.com"}
        }
    }
    

动态收集策略

可以在收集器中实现更复杂的逻辑:

  1. 基于环境变量控制收集
  2. 根据文件内容动态生成测试
  3. 实现测试依赖关系解析

最佳实践建议

  1. 保持兼容性:自定义收集器应优雅回退到默认行为
  2. 明确文档:为团队记录manifest文件的格式和使用方式
  3. 性能考虑:避免在收集阶段执行耗时操作
  4. 错误处理:妥善处理manifest文件解析错误

总结

通过自定义目录收集器,我们可以实现高度灵活的测试收集策略,满足各种复杂项目的需求。本文展示的manifest文件方案只是一个起点,开发者可以根据实际需求扩展出更强大的收集逻辑。

掌握这项技术后,你将能够:

  • 精确控制测试收集范围
  • 实现基于配置的测试过滤
  • 为不同目录应用不同的测试策略
  • 构建更智能的测试基础设施