Test Suite Setup
This page covers the one-time configuration needed to run function block tests with pytest.
Dependencies
Add test dependencies to your pyproject.toml:
[project.optional-dependencies]
test = [
"pytest>=8.0",
"pytest-asyncio>=0.24",
"neops-remote-lab", # only if using remote lab tests
]
Install with:
pytest Configuration
Add a [tool.pytest.ini_options] section to pyproject.toml:
[tool.pytest.ini_options]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
markers = [
"function_block: marks tests as function block tests",
]
Why asyncio_mode = "auto"?
With "auto", every async def test runs as async automatically — no need to
add @pytest.mark.asyncio to each function. The SDK decorators
(@fb_test_case, @fb_test_case_with_lab) also handle this internally, so
you get consistent behavior whether you write standalone tests or use the
decorator shortcut.
| Mode | Behavior | Trade-off |
|---|---|---|
auto (recommended) |
All async def tests run as async automatically |
Less boilerplate; matches the SDK's own config |
strict |
You must add @pytest.mark.asyncio to every async test |
Explicit — but easy to forget and get coroutine never awaited errors |
conftest.py Patterns
A typical conftest.py for function block projects:
import pytest
from neops_workflow_engine_client import DeviceTypeDto
from neops_worker_sdk.testing.factories.context_factory import create_workflow_context
@pytest.fixture
def cisco_device() -> DeviceTypeDto:
return DeviceTypeDto(
id=1,
hostname="core-rtr-01",
ip="10.0.1.1",
platform="ios",
username="admin",
password="secret",
)
@pytest.fixture
def device_context(cisco_device: DeviceTypeDto):
return create_workflow_context(
run_on="device",
entity_id=cisco_device.id,
devices=[cisco_device],
)
create_workflow_context builds a WorkflowContext from raw DTOs — the same
object your function block receives in run().
| Parameter | Type | Purpose |
|---|---|---|
run_on |
str |
Entity type: "device", "group", "interface", or "global" |
entity_id |
int \| float \| None |
ID of the primary entity the block runs on |
devices |
Iterable[DeviceTypeDto] |
Devices available in the context |
device_groups |
Iterable[DeviceGroupTypeDto] |
Groups available in the context |
interfaces |
Iterable[InterfaceTypeDto] |
Interfaces available in the context |
Environment Variables
| Variable | Purpose | Default |
|---|---|---|
BLOCKING_DETECTION_THRESHOLD |
Seconds before the blocking detector warns | 0.5 |
REMOTE_LAB_URL |
Base URL of the remote lab server | (unset — uses local labs) |
Running Tests
The test suite uses markers to separate tests into tiers. By default,
pytest runs unit tests and example function block tests — only remote lab
tests are excluded so you can iterate locally without network dependencies.
Test Tiers
| Marker | What it covers | When it runs |
|---|---|---|
| (none) | SDK unit tests in tests/ |
Always (default) |
examples |
Example function block tests in examples/ |
Always (default) |
function_block |
Function block lifecycle tests via @fb_test_case |
Always (default) |
remote_lab |
Integration tests against real devices via @fb_test_case_with_lab |
Opt-in: make test-function-blocks (requires REMOTE_LAB_URL) |
Commands
uv run pytest # unit tests + examples (default)
uv run pytest -m "examples and not remote_lab" # example tests only
uv run pytest -m "function_block or remote_lab" # integration tests (needs REMOTE_LAB_URL)
uv run pytest -m "" # everything
uv run pytest -k "test_config_backup" # by name pattern
Or use the Make targets:
make test # unit tests + examples (default)
make test-examples # example tests only
make test-function-blocks # function block + remote lab tests
make test-all # everything
Running all tests locally
To run the full suite including remote lab tests, set the lab URL first:
Marker Configuration
Markers are defined in pyproject.toml. The default addopts excludes only
remote_lab tests:
To override the default exclusion for a single run, pass -m explicitly —
it replaces the default:
uv run pytest -m "" # everything including remote lab
uv run pytest -m "remote_lab" # remote lab tests only
How markers are applied
@fb_test_caseautomatically marks tests withfunction_block.@fb_test_case_with_labautomatically marks tests with bothfunction_blockandremote_lab.- Tests collected from
examples/are automatically marked withexamplesvia the rootconftest.py.
Linting Tests
If you use ruff, ensure test files are not excluded: