"""Utility functions wrapping the excellent `mock` library.""" from __future__ import annotations from typing import Any from unittest.mock import ( ANY, MagicMock, Mock, PropertyMock, call, create_autospec, mock_open, patch, ) from pytest import FixtureRequest, LogCaptureFixture # noqa: PT013 __all__ = ( "ANY", "FixtureRequest", "LogCaptureFixture", "MagicMock", "Mock", "call", "class_mock", "function_mock", "initializer_mock", "instance_mock", "method_mock", "property_mock", ) def class_mock( request: FixtureRequest, q_class_name: str, autospec: bool = True, **kwargs: Any ) -> Mock: """Return mock patching class with qualified name `q_class_name`. The mock is autospec'ed based on the patched class unless the optional argument `autospec` is set to False. Any other keyword arguments are passed through to Mock(). Patch is reversed after calling test returns. """ _patch = patch(q_class_name, autospec=autospec, **kwargs) request.addfinalizer(_patch.stop) return _patch.start() def cls_attr_mock( request: FixtureRequest, cls: type, attr_name: str, name: str | None = None, **kwargs: Any, ): """Return a mock for attribute `attr_name` on `cls`. Patch is reversed after pytest uses it. """ name = request.fixturename if name is None else name _patch = patch.object(cls, attr_name, name=name, **kwargs) request.addfinalizer(_patch.stop) return _patch.start() def function_mock( request: FixtureRequest, q_function_name: str, autospec: bool = True, **kwargs: Any ): """Return mock patching function with qualified name `q_function_name`. Patch is reversed after calling test returns. """ _patch = patch(q_function_name, autospec=autospec, **kwargs) request.addfinalizer(_patch.stop) return _patch.start() def initializer_mock(request: FixtureRequest, cls: type, autospec: bool = True, **kwargs: Any): """Return mock for __init__() method on `cls`. The patch is reversed after pytest uses it. """ _patch = patch.object(cls, "__init__", autospec=autospec, return_value=None, **kwargs) request.addfinalizer(_patch.stop) return _patch.start() def instance_mock( request: FixtureRequest, cls: type, name: str | None = None, spec_set: bool = True, **kwargs: Any, ): """ Return a mock for an instance of `cls` that draws its spec from the class and does not allow new attributes to be set on the instance. If `name` is missing or |None|, the name of the returned |Mock| instance is set to *request.fixturename*. Additional keyword arguments are passed through to the Mock() call that creates the mock. """ name = name if name is not None else request.fixturename return create_autospec(cls, _name=name, spec_set=spec_set, instance=True, **kwargs) def loose_mock(request: FixtureRequest, name: str | None = None, **kwargs: Any): """ Return a "loose" mock, meaning it has no spec to constrain calls on it. Additional keyword arguments are passed through to Mock(). If called without a name, it is assigned the name of the fixture. """ if name is None: name = request.fixturename return Mock(name=name, **kwargs) def method_mock( request: FixtureRequest, cls: type, method_name: str, autospec: bool = True, **kwargs: Any, ): """Return mock for method `method_name` on `cls`. The patch is reversed after pytest uses it. """ _patch = patch.object(cls, method_name, autospec=autospec, **kwargs) request.addfinalizer(_patch.stop) return _patch.start() def open_mock(request: FixtureRequest, module_name: str, **kwargs: Any): """ Return a mock for the builtin `open()` method in `module_name`. """ target = "%s.open" % module_name _patch = patch(target, mock_open(), create=True, **kwargs) request.addfinalizer(_patch.stop) return _patch.start() def property_mock(request: FixtureRequest, cls: type, prop_name: str, **kwargs: Any): """ Return a mock for property `prop_name` on class `cls` where the patch is reversed after pytest uses it. """ _patch = patch.object(cls, prop_name, new_callable=PropertyMock, **kwargs) request.addfinalizer(_patch.stop) return _patch.start() def var_mock(request: FixtureRequest, q_var_name: str, **kwargs: Any): """ Return a mock patching the variable with qualified name `q_var_name`. Patch is reversed after calling test returns. """ _patch = patch(q_var_name, **kwargs) request.addfinalizer(_patch.stop) return _patch.start()