labthings_fastapi.testing ========================= .. py:module:: labthings_fastapi.testing .. autoapi-nested-parse:: Test harnesses to help with writitng tests for things.. Attributes ---------- .. autoapisummary:: labthings_fastapi.testing.Params labthings_fastapi.testing.ReturnType labthings_fastapi.testing.ThingSubclass Classes ------- .. autoapisummary:: labthings_fastapi.testing.MockThingServerInterface Functions --------- .. autoapisummary:: labthings_fastapi.testing.create_thing_without_server labthings_fastapi.testing._mock_slots labthings_fastapi.testing.use_dummy_url_for Module Contents --------------- .. py:data:: Params .. py:data:: ReturnType .. py:class:: MockThingServerInterface(name: str, settings_folder: str | None = None) Bases: :py:obj:`labthings_fastapi.thing_server_interface.ThingServerInterface` A mock class that simulates a ThingServerInterface without the server. This allows a `.Thing` to be instantiated but not connected to a server. The methods normally provided by the server are mocked, specifically: * The `name` is set by an argument to `__init__`\ . * `start_async_task_soon` silently does nothing, i.e. the async function will not be run. * The settings folder will either be specified when the class is initialised, or a temporary folder will be created. * `get_thing_states` will return an empty dictionary. Initialise a ThingServerInterface. :param name: The name of the Thing we're providing an interface to. :param settings_folder: The location where we should save settings. By default, this is a temporary directory. .. py:attribute:: _name :type: str .. py:attribute:: _settings_tempdir :type: tempfile.TemporaryDirectory | None :value: None .. py:attribute:: _settings_folder :value: None .. py:attribute:: _mocks :type: list[unittest.mock.Mock] :value: [] .. py:method:: start_async_task_soon(async_function: Callable[Params, Awaitable[ReturnType]], *args: Any) -> concurrent.futures.Future[ReturnType] Do nothing, as there's no event loop to use. This returns a `concurrent.futures.Future` object that is already cancelled, in order to avoid accidental hangs in test code that attempts to wait for the future object to resolve. Cancelling it may cause errors if you need the return value. If you need the async code to run, it's best to add the `.Thing` to a `lt.ThingServer` instead. Using a test client will start an event loop in a background thread, and allow you to use a real `.ThingServerInterface` without the overhead of actually starting an HTTP server. :param async_function: the asynchronous function to call. :param \*args: positional arguments to be provided to the function. :returns: a `concurrent.futures.Future` object that has been cancelled. .. py:property:: settings_folder :type: str The path to a folder where persistent files may be saved. This will create a temporary folder the first time it is called, and return the same folder on subsequent calls. :returns: the path to a temporary folder. .. py:property:: path :type: str The path, relative to the server's base URL, of the Thing. A ThingServerInterface is specific to one Thing, so this path points to the base URL of the Thing, i.e. the Thing Description's endpoint. .. py:method:: get_thing_states() -> Mapping[str, Any] Return an empty dictionary to mock the metadata dictionary. :returns: an empty dictionary. .. py:property:: _action_manager :type: labthings_fastapi.actions.ActionManager :abstractmethod: Raise an error, as there's no action manager without a server. :raises NotImplementedError: always. .. py:property:: application_config :type: None Return an empty application configuration when mocking. :return: None .. py:data:: ThingSubclass .. py:function:: create_thing_without_server(cls: type[ThingSubclass], *args: Any, settings_folder: str | None = None, mock_all_slots: bool = False, **kwargs: Any) -> ThingSubclass Create a `.Thing` and supply a mock ThingServerInterface. This function is intended for use in testing, where it will enable a `.Thing` to be created without a server, by supplying a `.MockThingServerInterface` instead of a real `.ThingServerInterface`\ . The name of the Thing will be taken from the class name, lowercased. :param cls: The `.Thing` subclass to instantiate. :param \*args: positional arguments to ``__init__``. :param settings_folder: The path to the settings folder. A temporary folder is used by default. :param mock_all_slots: Set to True to create a `unittest.mock.Mock` object connected to each thing slot. It follows the default of the specified to the slot. So if an optional slot has a default of `None`, no mock will be provided. :param \**kwargs: keyword arguments to ``__init__``. :returns: an instance of ``cls`` with a `.MockThingServerInterface` so that it will function without a server. :raises ValueError: if a keyword argument called 'thing_server_interface' is supplied, as this would conflict with the mock interface. .. py:function:: _mock_slots(thing: labthings_fastapi.thing.Thing) -> None Mock the slots of a thing created by create_thing_without_server. :param thing: The thing to mock the slots of. :raises TypeError: If this was called on a Thing with a real ThingServerInterface .. py:function:: use_dummy_url_for() -> collections.abc.Iterator[None] Use the dummy URL for function in the context variable.