Sardana testing
Overview
pytest is the test runner for the whole Sardana suite. Existing
unittest.TestCase tests are still collected by pytest, but new tests should
use pytest functions and fixtures.
Tests live next to the code in test subpackages. Resources needed by tests
live in test/res subdirectories where appropriate. Bug fixes should
normally include a regression test.
The test infrastructure originated in Sardana Enhancement Proposal 5. See the maintained Sardana Enhancement Proposal index for SEP5 and the other design documents.
Install the test environment
Run all commands from the repository root. Pixi installs the project and the supported test dependencies before running a task.
If your environment does not use Pixi, install the project with its supported test dependencies using pip:
python -m pip install -e ".[test]"
Optional GUI, recorder, or backend tests can still require their own optional dependencies.
Run the tests
Purpose |
Command |
Expected scope |
|---|---|---|
Whole suite |
|
All collected tests; optional tests may skip when their dependencies or explicit opt-ins are absent. |
No Tango/Redis services or GUI stack |
|
Excludes |
Database integration |
|
Selects |
Device-server system tests |
|
Selects |
Coverage |
|
Writes the HTML report to |
Direct pytest
After installing the test dependencies with pip, run the direct equivalents
from the repository root. This ensures that pytest.ini and
src/sardana/conftest.py are discovered:
python -m pytest
python -m pytest -m "not tango and not device_server and not gui and not redis"
python -m pytest -m "tango and not device_server"
python -m pytest -m device_server
Using python -m pytest runs pytest with the interpreter used by
python -m pip. The equivalent pytest executable also works when it is
on PATH.
Target tests
Pass normal pytest arguments through Pixi to run a directory, file, individual test, or marker expression:
pixi run pytest -v src/sardana/pool/test
pixi run pytest src/sardana/test/test_tango_db.py::test_tango_db_is_reachable
pixi run pytest -m "tango and not device_server"
Without Pixi, use the same arguments with the active Python interpreter:
python -m pytest -v src/sardana/pool/test
python -m pytest src/sardana/test/test_tango_db.py::test_tango_db_is_reachable
The project pytest.ini applies short tracebacks, importlib import mode,
strict marker checking, skip reporting, and a 600-second per-test timeout.
Test categories and markers
Selection markers group tests by the runtime services and optional stacks they need:
Marker |
Meaning |
|---|---|
|
Requires a Tango database. |
|
Starts a Pool and/or MacroServer device server. |
|
Requires the Qt GUI stack. |
|
Requires |
Following the current suite convention, mark a device-server test with both
tango and device_server. The tango marker labels a test for
selection; the test must still request tango_db, session_tango_db, or a
fixture that depends on one of them. The device_server marker automatically
activates session_tango_db for compatibility with both pytest and old
unittest-style tests.
Register every new marker in pytest.ini or in the plugin that consumes it,
because strict marker checking is enabled. The fixture-configuration markers
attrs, attribute_values, and kwargs are summarized in the
controller testing guide for in-process Pool
fixtures. The repository-local Tango fixture keys are described below.
Tests that need a Tango database skip when neither an isolated database nor an opted-in external database can be provided.
Tango database isolation
Important
By default the test fixtures do not use the database selected by an
ambient TANGO_HOST. They start isolated pytango-db databases
instead. To use an existing database, set both TANGO_HOST and
SARDANA_TEST_EXTERNAL_DB. Use only a disposable test database because
the suite creates and deletes server, device, alias, and property records.
Use tango_db only for tests that access the database through
tango.Database(). It starts a fresh database for each test. Use
session_tango_db, normally through a device-server fixture, whenever a
test creates a DeviceProxy or starts a server. The Tango client process
binds proxy resolution to the first database it uses, so a per-test
database is not suitable for proxies.
The external-database opt-in is explicit:
TANGO_HOST=127.0.0.1:10000 SARDANA_TEST_EXTERNAL_DB=1 \
pixi run test-system
The session database is shared. Custom server, device, controller, alias, and element names must be unique and must be removed during teardown.
Choose a fixture
Fixture |
Scope |
Provides / intended use |
|---|---|---|
Pool core fixtures such as |
function |
In-process Pool objects. No Tango database or server. See the controller testing guide and the Pool test API. |
|
function |
Fresh isolated database and its |
|
session |
Shared database for device proxies and device servers. Usually reached through another fixture. |
|
function |
A running Pool device name. |
|
function |
An object with |
|
function |
A running Door name connected to the fixture Pool. |
|
function |
Registered macro executor connected to the fixture Door. |
|
function |
Pool plus standard dummy controllers and elements. Translate logical
|
|
function |
|
The repository-local src/sardana/conftest.py fixtures are provisional.
They are discovered for the Sardana source suite and are not the pytest plugin
exported to third-party projects. The separately exported plugin is
sardana.pool.test.util, registered by the pytest11 entry point in
setup.py.
The session-scoped create_tango_pool, create_tango_macroserver, and
create_sar_demo chain is specialized support for the existing sar_demo
macro tests. It is not the default for a new isolated test.
Configure Tango fixtures
Repository-local Tango fixtures consume these keys from the closest
kwargs marker:
tango_poolandtango_pool_serveraccept apropertiesmapping for Pool device properties.tango_macroserveraccepts apropertiesmapping for MacroServer device properties such asMacroPath.sar_test_elementsacceptsextra_pseudo_controllerswith additional pseudo-controller definitions.
For example, provide a custom macro path to the MacroServer used by
door_macro_executor:
1@pytest.mark.kwargs(
2 {
3 "tango_macroserver": {
4 "properties": {"MacroPath": ["/path/to/macros"]}
5 }
6 }
7)
8def test_custom_macro(door_macro_executor):
9 ...
Write a device-server test
Use the fixture-provided Pool name to create a device proxy:
1import pytest
2import tango
3
4pytestmark = [pytest.mark.tango, pytest.mark.device_server]
5
6
7def test_pool_is_running(tango_pool):
8 pool = tango.DeviceProxy(tango_pool)
9 assert pool.state() == tango.DevState.ON
Macro tests can use the fixture-provided executor directly:
1import pytest
2
3from sardana.macroserver.macros.test import MacroTester
4
5pytestmark = [pytest.mark.tango, pytest.mark.device_server]
6
7
8def test_macro_runs(door_macro_executor):
9 MacroTester(door_macro_executor).macro_runs(macro_name="lsdef")
The fixtures own server cleanup and the lifecycle of isolated databases. An explicitly selected external database remains running. A test still owns any custom element or external-backend data that it creates outside the fixtures.
Test elements and parallel sessions
sar_test_elements uses session-unique physical names. Translate historical
logical names before using them:
motor_name = sar_test_elements.name("_test_mt_1_1")
Do not rely on a concrete translated value because it changes per process. This translation allows concurrent sessions to share an explicitly selected database without name collisions.
Macro and controller test helpers
For new macro tests, combine door_macro_executor with
MacroTester. BaseMacroTestCase, RunMacroTestCase,
and RunStopMacroTestCase remain available for existing class-based
tests.
Controller plugin authors should use the controller testing guide. Its in-process Pool fixtures are the exported third-party pytest plugin.
Legacy device-server base classes
The following classes remain for backward compatibility only:
Deprecated class |
Replacement |
|---|---|
|
|
|
|
|
|