customdirectory.rst

Overview

This documentation describes the **custom directory collector** pattern for pytest, which enables users to customize how pytest collects test files within specific directories. By default, pytest collects directories as either packages (directories containing `__init__.py`) or simple directories. However, some projects require finer control over which files are collected as tests in a directory, for instance, to restrict test collection to an explicit list of files.

This file presents a practical example of implementing a custom directory collector that reads a `manifest.json` file inside the directory. The manifest explicitly lists test files to be collected, which allows deterministic and reproducible test discovery on a per-directory basis.

The approach involves creating a subclass of `pytest.Directory` (named `ManifestDirectory`) and hooking it into the pytest collection process with the `pytest_collect_directory` hook. This collector reads the manifest and yields only the specified test files, ignoring all others in that directory.


Detailed Explanation

Classes and Functions

ManifestDirectory (class)

class ManifestDirectory(pytest.Directory):
    def collect(self):
        manifest_path = self.path / "manifest.json"
        manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
        ihook = self.ihook
        for file in manifest["files"]:
            yield from ihook.pytest_collect_file(
                file_path=self.path / file, parent=self
            )

This class is instantiated automatically by the hook below when a directory with a manifest is encountered.


pytest_collect_directory(path, parent) (hook implementation)

@pytest.hookimpl
def pytest_collect_directory(path, parent):
    if path.joinpath("manifest.json").is_file():
        return ManifestDirectory.from_parent(parent=parent, path=path)
    return None

Implementation Details

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

It lists the relative paths of files within the directory that pytest should collect as test modules.


How This File Interacts with the System


Example Workflow and Usage

Suppose you have this directory structure:

customdirectory/
├── conftest.py
├── tests/
│   ├── manifest.json
│   ├── test_first.py
│   ├── test_second.py
│   └── test_third.py

Visual Diagram

Below is a Mermaid class diagram illustrating the main class and hook in this file:

classDiagram
    class ManifestDirectory {
        +collect()
        -path
        -ihook
    }
    class pytest {
        <<hook>>
        +pytest_collect_directory(path, parent)
    }

    pytest ..> ManifestDirectory : creates instance if manifest.json exists

This diagram shows that the `pytest_collect_directory` hook function decides whether to instantiate `ManifestDirectory` based on the presence of a manifest file, and that the `ManifestDirectory` class implements the `collect()` method to yield test modules.


Summary

This file exemplifies a pytest customization that empowers users to define explicit test discovery rules per directory using a manifest JSON file. By leveraging pytest’s extensible collection hooks and subclassing, it enables:

This technique fits naturally into pytest’s plugin architecture and can be extended with richer manifest schemas (e.g., glob patterns, exclusions) or additional directory-based rules.

For further details, see the example `conftest.py` and test files provided in the referenced `customdirectory` example project.