Unit Testing
Relevant source files
The following files were used as context for generating this wiki page:
Unit testing in nornir-infrahub validates the core logic, plugin functionality, and error handling of the project without requiring external dependencies like Docker or network connectivity. These tests ensure that the inventory and task plugins behave correctly across various scenarios, such as configuration mapping resolution and artifact management.
The project uses pytest as the primary testing framework, supplemented by pytest-asyncio for asynchronous test support. Unit tests are located in the tests/unit/ directory and are designed to run quickly by mocking all Infrahub API interactions.
Sources: dev/guidelines/testing.md:1-12, AGENTS.md:27-33
Testing Philosophy and Environment
Unit tests represent the default testing layer for the repository. They focus on verifying plugin logic, data validation, and branching paths within the code. Unlike integration tests, unit tests must not touch the network or require a live Infrahub instance.
Execution Methods
You can execute unit tests using several methods:
| Method | Command | Description |
|---|---|---|
| Default Pytest | pytest | Runs all unit tests; excludes integration tests by default. |
| Specific File | pytest tests/unit/test_inventory.py | Runs tests only within the specified file. |
| Specific Function | pytest tests/unit/test_inventory.py::test_func | Targets a single test function for rapid feedback. |
| Invoke Task | invoke lint | Includes static analysis gates that precede test execution. |
Sources: dev/guidelines/testing.md:9-12, dev/guides/local-development-setup.md:39-45, AGENTS.md:37-40
Configuration
The pyproject.toml file configures pytest to exclude integration tests by default using the addopts = "-m 'not integration'" setting. This ensures that a standard test run remains fast and hermetic.
Sources: pyproject.toml:57-62, dev/guidelines/testing.md:9-12
Mocking Strategies
Because unit tests cannot communicate with a real Infrahub server, the project employs two distinct mocking styles to simulate SDK behavior.
1. Real SDK Objects with No I/O
This style is used for logic that operates on InfrahubNode objects and their schemas, such as mapping resolution. Tests construct a real InfrahubNodeSync using a client pointed at a dummy address (e.g., http://mock). This allows the code to exercise real Pydantic models and SDK logic without triggering network calls.
2. MagicMock for Clients and Nodes
This style is primarily used for task plugins. A fake Nornir Task object is created where the task.host.data["InfrahubNode"] is a MagicMock. The SDK client is driven off this mock, allowing tests to stub return values for methods like client.create or client.get.
The diagram shows how mocks isolate plugin logic from the Infrahub API during unit tests.
Sources: dev/guidelines/testing.md:20-33, tests/unit/test_file_object.py:44-53
Testing Task Plugins
Task plugins are tested by inspecting the returned Nornir Result object. Tests verify the following fields:
result.failed: Ensures the task correctly reports success or failure.result.changed: Validates idempotency (e.g., file uploads skip if the checksum matches).result.result: Checks that the payload or error message matches expectations.
Error Handling in Tests
Task behavior dictates how errors are asserted:
- File Object Tasks: These tasks catch SDK exceptions and return a failed
Result. Tests inspect theResultobject rather than usingpytest.raises. - Artifact Tasks: These tasks allow errors to propagate. Tests use
pytest.raisesto verifyRuntimeErrororValueErrorexceptions.
Sources: dev/guidelines/testing.md:39-50, dev/guidelines/python.md:73-77
Development Workflow and Gates
Unit tests are part of a larger quality assurance suite that includes formatting, linting, and type checking.
Pre-commit Checklist
- Formatting: Run
invoke formatto align code withruffandrumdlstandards. - Linting: Run
invoke lintto executeyamllint,ruff,pylint,ty, andrumdl. - Type Checking: Ensure
ty checkpasses for all public function signatures. - Unit Tests: Run
pytestto confirm all local logic is functional.
This flow represents the mandatory local verification process before committing code.
Sources: tasks.py:18-67, dev/guidelines/python.md:12-25, AGENTS.md:92-95
Conventions and Constraints
- Mocking Imports: Magic numbers (
PLR2004) and deferred imports (PLC0415) are permitted intests/**to allow for flexible mocking andunittest.mockusage inside test bodies. - Docstrings: Test functions and private helpers do not require docstrings.
- Hermeticity: Use the
tmp_pathfixture for any filesystem operations to ensure tests do not leave artifacts on the host machine. - Parameterized Tests: Use
@pytest.mark.parametrizefor tabular test cases to cover multiple input variations efficiently.
Sources: dev/guidelines/testing.md:52-73, pyproject.toml:132-133
Unit testing provides the first line of defense in the nornir-infrahub project. By strictly isolating plugin logic from the Infrahub server through comprehensive mocking, these tests allow developers to validate complex mapping and artifact management logic with high speed and reliability.