Testing Your Function Block
Function blocks interact with real network devices -- testing locally catches bugs before they reach production. A few well-placed tests save hours of debugging on live infrastructure.
Quick Setup
Install the test dependencies as optional extras:
Add the pytest configuration to your pyproject.toml:
Note
The full pyproject.toml from the Setup page already includes this
configuration and the test dependencies.
With asyncio_mode = "auto", every async def test runs as async
automatically — no need for @pytest.mark.asyncio on each function.
The SDK decorators (@fb_test_case, @fb_test_case_with_lab) also
handle this internally.
Writing a Standalone Test
The pattern for testing a function block is straightforward:
- Build a
WorkflowContext-- the data envelope your function block receives at runtime. - Instantiate your function block class.
- Call
execute_function_block()with parameters and context. - Assert against the result.
Start with a helper that creates a minimal context:
def _create_context() -> WorkflowContext:
return WorkflowContext(
JobExecutionContextDto(devices=[], deviceGroups=[], interfaces=[]),
run_on="global",
)
WorkflowContext is the data envelope that neops passes to your function block at
runtime — it contains device info, credentials, and workflow metadata. For unit tests
of a simple block like Echo, an empty context is enough since the block does not
access any device data.
Now write the actual test:
async def test_echo_returns_same_text() -> None:
block = Echo()
result = await block.execute_function_block(
params=EchoParameters(message="hello"),
context=_create_context(),
)
assert result.success
assert result.data is not None
assert result.data.echoed_message == "hello"
The test creates an Echo instance, executes it with text="hello", and verifies that
the result is successful and contains the expected output. Run it with:
Tip
execute_function_block() is an async method, so every test function needs
async def. With asyncio_mode = "auto" this works out of the box — no
extra decorator required.
The Decorator Shortcut
For quick smoke tests, the SDK provides @fb_test_case -- a decorator that attaches
test cases directly to your function block class. Pytest discovers and runs them
automatically.
@fb_test_case(
"Echo test",
EchoParameters(text="hello"),
context=ctx,
assertions=[lambda r: r.success],
)
class Echo(FunctionBlock[EchoParameters, EchoResult]):
...
The assertions parameter takes a list of check functions. Each receives the result
and should return True if the check passes. lambda r: r.success is shorthand for
"check that the result was successful." You could also write it as a regular function:
Behind the scenes, the decorator generates a test function and registers it in the module. You get the same result as writing the test by hand, with less boilerplate.
When to use standalone tests vs. the decorator
Use standalone tests when you need complex setup, multiple assertions across
different scenarios, or shared fixtures. Use @fb_test_case for straightforward
pass/fail checks that live next to the function block definition.
Testing with Real Devices
The SDK includes @fb_test_case_with_lab for integration testing against actual network
equipment. The remote lab provisions Netlab /
Containerlab topologies on demand -- Cisco IOL routers,
FRR instances, and more -- so your function blocks run against genuine device responses.
To enable it:
- Set the
REMOTE_LAB_URLenvironment variable to your lab endpoint. - Decorate your function block with
@fb_test_case_with_lab, specifying the parameters and assertions. - The decorator handles topology provisioning, device context creation, and teardown.
Note
Remote lab tests require network access to the lab environment. They are best suited for CI pipelines or dedicated test environments. See the full Testing section for setup details and topology configuration.
Run Your Tests
Execute the full test suite with verbose output:
Expected output looks like:
========================= test session starts ==========================
collected 2 items
test_echo.py::test_echo_returns_same_text PASSED [ 50%]
test_echo.py::test_echo_handles_empty_string PASSED [100%]
========================= 2 passed in 0.12s ============================
You've completed the getting started guide. Explore the deep dives for more detail:
- Function Blocks -- parameters, results, registration, and lifecycle
- Device Connections -- opening sessions, running commands, parsing output
- Testing -- remote lab setup, fixtures, and advanced assertions