.. highlight:: python :linenothreshold: 5 .. currentmodule:: sardana.macroserver.macros.test .. _sardana-test-howto: =============== Sardana testing =============== .. _sardana-test-general-howto: 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 :ref:`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: .. code-block:: shell python -m pip install -e ".[test]" Optional GUI, recorder, or backend tests can still require their own optional dependencies. .. _sardana-test-run-commands: Run the tests ------------- .. list-table:: Test commands :header-rows: 1 :widths: 20 28 52 * - Purpose - Command - Expected scope * - Whole suite - ``pixi run test`` - All collected tests; optional tests may skip when their dependencies or explicit opt-ins are absent. * - No Tango/Redis services or GUI stack - ``pixi run test-unit`` - Excludes ``tango``, ``device_server``, ``gui``, and ``redis``; no service needs to be provided or started. * - Database integration - ``pixi run test-tango-db`` - Selects ``tango and not device_server`` and normally starts a fresh isolated ``pytango-db`` database per test. * - Device-server system tests - ``pixi run test-system`` - Selects ``device_server`` and normally uses a session-scoped isolated ``pytango-db`` database. * - Coverage - ``pixi run test-cov`` - Writes the HTML report to ``htmlcov/``. 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: .. code-block:: shell 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: .. code-block:: shell 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: .. code-block:: shell 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: .. list-table:: Selection markers :header-rows: 1 :widths: 25 75 * - Marker - Meaning * - ``tango`` - Requires a Tango database. * - ``device_server`` - Starts a Pool and/or MacroServer device server. * - ``gui`` - Requires the Qt GUI stack. * - ``redis`` - Requires ``TEST_REDIS_URI`` and a disposable Redis server. 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 :ref:`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: .. code-block:: shell 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 ---------------- .. list-table:: Test fixtures :header-rows: 1 :widths: 27 13 60 * - Fixture - Scope - Provides / intended use * - Pool core fixtures such as ``pool``, ``mot01``, and ``ct01`` - function - In-process Pool objects. No Tango database or server. See the :ref:`controller testing guide ` and the :doc:`Pool test API <../../reference/api/sardana/pool/test>`. * - ``tango_db`` - function - Fresh isolated database and its ``host:port``; database-only tests. * - ``session_tango_db`` - session - Shared database for device proxies and device servers. Usually reached through another fixture. * - ``tango_pool`` - function - A running Pool device name. * - ``tango_pool_server`` - function - An object with ``device_name`` and ``starter`` attributes, for tests that must restart or inspect the Pool server. * - ``tango_macroserver`` - function - A running Door name connected to the fixture Pool. * - ``door_macro_executor`` - function - Registered macro executor connected to the fixture Door. * - ``sar_test_elements`` - function - Pool plus standard dummy controllers and elements. Translate logical ``_test_*`` names with ``sar_test_elements.name(...)``. * - ``measurement_group`` - function - ``MeasurementGroupHelper`` bound to ``sar_test_elements``; cleanup is automatic. 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_pool`` and ``tango_pool_server`` accept a ``properties`` mapping for Pool device properties. * ``tango_macroserver`` accepts a ``properties`` mapping for MacroServer device properties such as ``MacroPath``. * ``sar_test_elements`` accepts ``extra_pseudo_controllers`` with additional pseudo-controller definitions. For example, provide a custom macro path to the MacroServer used by ``door_macro_executor``: .. code-block:: python @pytest.mark.kwargs( { "tango_macroserver": { "properties": {"MacroPath": ["/path/to/macros"]} } } ) def test_custom_macro(door_macro_executor): ... Write a device-server test -------------------------- Use the fixture-provided Pool name to create a device proxy: .. code-block:: python import pytest import tango pytestmark = [pytest.mark.tango, pytest.mark.device_server] def test_pool_is_running(tango_pool): pool = tango.DeviceProxy(tango_pool) assert pool.state() == tango.DevState.ON Macro tests can use the fixture-provided executor directly: .. code-block:: python import pytest from sardana.macroserver.macros.test import MacroTester pytestmark = [pytest.mark.tango, pytest.mark.device_server] def test_macro_runs(door_macro_executor): 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: .. code-block:: python 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 :class:`MacroTester`. :class:`BaseMacroTestCase`, :class:`RunMacroTestCase`, and :class:`RunStopMacroTestCase` remain available for existing class-based tests. Controller plugin authors should use the :ref:`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: .. list-table:: Device-server test class replacements :header-rows: 1 :widths: 40 60 * - Deprecated class - Replacement * - ``BasePoolTestCase`` - ``tango_pool`` or ``tango_pool_server`` * - ``SarTestTestCase`` - ``sar_test_elements`` * - ``BaseMacroServerTestCase`` - ``tango_macroserver`` and, when executing macros, ``door_macro_executor``