Pytest Arch
pytestarch is a Python test framework that enables static analysis of software architecture by defining and enforcing rules based on import dependencies between modules. It integrates seamlessly with pytest to ensure architectural constraints are met. The current version is 4.0.1, with major releases occurring periodically to introduce new features and adapt to Python version changes.
Common errors
-
KeyError: 'module_name'
cause A module was discovered by pytestarch but does not match any defined `Layer` module path regex, or there's an internal issue with submodule import resolution (particularly in v4.0.0).fixReview your `get_layers` definitions and the regexes for each `Layer`. Ensure every top-level module (or submodule intended for a layer) has a matching `Layer` entry. Check for typos in module paths or regexes. If on v4.0.0, consider upgrading to v4.0.1+. -
AssertionError: The following architectural rules were violated: [... 'module.path' is not part of a layer ...]
cause pytestarch found modules in your specified `root_path` (or `PYTEST_ARCH_ROOT`) that are not covered by any `Layer` definition you provided.fixEither add the missing modules to an existing `Layer` definition or create a new `Layer` for them. Alternatively, if these modules should be ignored by architectural checks, use `architectural_rule.ignore_modules` to exclude them. -
AssertionError: The following architectural rules were violated: [... 'module_a' imports from 'module_b' which is not allowed.]
cause A specific module import in your codebase violates one of the `should_not_import_modules_of_other_layers()`, `may_only_import_modules_of_layers()`, or `can_only_import_modules_of_layers()` rules you have defined.fixInspect the reported import. You must either refactor the code to remove the disallowed import, or adjust your architectural rules to explicitly permit the import if it represents a valid and intended dependency. -
ModuleNotFoundError: No module named 'src.your_module'
cause pytestarch cannot find or correctly resolve your Python modules, typically because the base path (`PYTEST_ARCH_ROOT` or `root_path` in `get_layers`) is incorrect or misaligned with your project's import structure.fixEnsure the `PYTEST_ARCH_ROOT` environment variable is correctly set to the absolute path of your source code directory (e.g., `export PYTEST_ARCH_ROOT=$(pwd)/src`). Also, verify that the `root_path` passed to `get_layers` matches how your modules are imported (e.g., `get_layers('src', ...)` if your imports are `from src.module`). You may also need to ensure your project root is on `PYTHONPATH` for `pytest` itself to discover modules.
Warnings
- breaking The mechanism of exporting library modules changed in v4.0.0. This could break advanced integrations or direct internal module imports that relied on previous export paths.
- breaking Support for Python 3.8 was officially dropped in version 3.0.0. Attempting to use pytestarch v3.0.0 or later with Python 3.8 will result in compatibility errors.
- gotcha pytestarch relies heavily on the `PYTEST_ARCH_ROOT` environment variable to correctly identify the root of your source code. If this is not set or set incorrectly, module discovery and rule evaluation will fail, often leading to `ModuleNotFoundError` or `KeyError`.
- gotcha Failure to include all relevant top-level modules or submodules in your `Layer` definitions, especially when using regular expressions, can lead to `KeyError` or modules being incorrectly categorized as 'not part of a layer'. This was specifically addressed for certain `KeyError` scenarios in v4.0.1.
Install
-
pip install pytestarch
Imports
- architectural_rule
from pytestarch import architectural_rule
- Rule
from pytestarch import Rule
- Layer
from pytestarch.patterns import Layer
- get_layers
from pytestarch.patterns import get_layers
Quickstart
# Assuming a project structure like:
# project_root/
# ├── src/
# │ ├── __init__.py
# │ ├── domain/
# │ │ ├── __init__.py
# │ │ └── core.py
# │ ├── application/
# │ │ ├── __init__.py
# │ │ └── service.py # e.g., 'from src.domain import core'
# │ └── infrastructure/
# │ ├── __init__.py
# │ └── db.py # e.g., 'from src.domain import core'
# └── tests/
# └── test_architecture.py
# The PYTEST_ARCH_ROOT environment variable MUST be set to the path of your
# source code root (e.g., './src') for pytestarch to discover modules correctly.
# In tests/test_architecture.py
from pytestarch import architectural_rule
from pytestarch.patterns import Layer, get_layers
def test_layer_dependencies():
# Define the layers in your application.
# The first argument to get_layers is the base directory to scan for modules.
# The second argument is a list of Layer objects.
# Each Layer needs a unique name and a module path regex (e.g., 'src.domain').
layers = get_layers(
"src", # Base directory relative to where pytest is run, or an absolute path
[
Layer("domain", "src.domain"),
Layer("application", "src.application"),
Layer("infrastructure", "src.infrastructure"),
]
)
# Define architectural rules:
# 1. All modules discovered must belong to one of the defined layers.
# 2. Layers should not import modules from other layers by default.
# 3. Specifically, 'application' and 'infrastructure' layers are allowed
# to import *only* from the 'domain' layer.
architectural_rule.modules_belong_to_layers(layers) \
.should_not_import_modules_of_other_layers() \
.may_only_import_modules_of_layers("domain") \
.in_layers("application", "infrastructure") \
.check()
# To run this test, navigate to your project_root and execute in your terminal:
# PYTEST_ARCH_ROOT=./src pytest tests/test_architecture.py