This collection supports three types of tests, each running in Docker via docker-compose.yml:
| Type | Target | What it tests |
|---|---|---|
| Sanity | sanity |
Ansible module standards, Python compatibility, import checks |
| Unit | unit |
Individual functions/classes with mocked dependencies |
| Integration | integration |
End-to-end against a running Infrahub instance |
# Run all tests
invoke tests-all
# Run individual test types
invoke tests-sanity
invoke tests-unit
invoke tests-integration# Sanity tests
docker compose up --build --force-recreate --quiet-pull --exit-code-from sanity sanity
# Unit tests
docker compose up --build --force-recreate --quiet-pull --exit-code-from unit unit
# Integration tests
docker compose up --build --force-recreate --quiet-pull --exit-code-from integration integration| Variable | Purpose | Default |
|---|---|---|
PYTHON_VER |
Python version for Docker build | 3.12 |
ANSIBLE_SANITY_ARGS |
Extra args for ansible-test sanity |
empty |
ANSIBLE_UNIT_ARGS |
Extra args for unit tests | empty |
ANSIBLE_INTEGRATION_ARGS |
Extra args for integration tests | empty |
The Dockerfile uses multi-stage builds:
base— Installs Python, uv, and all dependenciessanity— Builds the collection, installs it, runsansible-test sanityunittests— Runs unit testsintegration— Runs integration tests (with network access for Infrahub)
Sanity tests use ansible-test sanity which checks:
- Module documentation format
- Python import correctness
- Required boilerplate (
__metaclass__ = type,__future__imports) - Plugin interface compliance
The pep8 test is skipped (Ruff handles this).
RUN ansible-test sanity $ANSIBLE_SANITY_ARGS \
--requirements \
--skip-test pep8 \
--python ${PYTHON_VERSION} \
plugins/tests/
unit/
plugins/
module_utils/
test_infrahub_utils.py
modules/
test_node.py
# pyproject.toml
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
pythonpath = ["."]Since tests run without an Infrahub instance, mock the SDK client:
from unittest.mock import MagicMock, patch
@patch("ansible_collections.opsmill.infrahub.plugins.module_utils.infrahub_utils.InfrahubClientSync")
def test_wrapper_creation(mock_client_class):
mock_client_class.return_value = MagicMock()
wrapper = InfrahubclientWrapper(
api_endpoint="https://infrahub.example.com",
token="test-token",
branch="main",
timeout=10,
validate_certs=True,
display=MagicMock(),
)
assert wrapper.client is not NoneUse ansible.module_utils.basic.AnsibleModule with mocked exit_json and fail_json:
from unittest.mock import patch, MagicMock
def test_node_module_create():
mock_module = MagicMock()
mock_module.params = {
"api_endpoint": "https://infrahub.example.com",
"token": "test",
"state": "present",
"kind": "BuiltinTag",
"data": {"name": "test-tag"},
"branch": "main",
}
# ... setup mocks and assert behaviorpytest = "^9.0.2"
pytest-mock = "*"
pytest-xdist = "*" # Parallel test execution
pytest-pythonpath = "*" # pythonpath support
mock = "^5.2.0"Integration tests require a running Infrahub instance. They use the integration_network Docker network to communicate.
tests/
integration/
targets/
<test_name>/
tasks/
main.yml
Integration tests are Ansible playbooks that exercise the full module → API path.
Tests run on every PR to develop:
- Linter job — Ruff check + format, yamllint, Vale
- Ansible linter and tests job — ansible-lint, sanity tests, unit tests
The CI workflow is in .github/workflows/trigger-pr-develop.yml.