Skip to content

API reference: servicewright.testing

In-memory doubles for testing services, entrypoints and containers. No dependencies, no extra.

See the testing guide for how they fit together.

In-memory test doubles for the servicewright kernel.

FakeSettings

Minimal settings double whose observability sections are all disabled.

Source code in servicewright/testing.py
class FakeSettings:
    """Minimal settings double whose observability sections are all disabled."""

    logging: Any = None
    error_tracking: Any = None
    tracing: Any = None
    metrics: Any = None

    def get_app_version(self) -> str:
        """Return a placeholder version."""
        return "0.0.0-test"

get_app_version()

Return a placeholder version.

Source code in servicewright/testing.py
def get_app_version(self) -> str:
    """Return a placeholder version."""
    return "0.0.0-test"

FakeScope

In-memory scope whose get resolves from a provided mapping.

Source code in servicewright/testing.py
class FakeScope:
    """In-memory scope whose ``get`` resolves from a provided mapping."""

    def __init__(self, provides: Mapping[Any, Any] | None = None) -> None:
        self._provides: dict[Any, Any] = dict(provides) if provides else {}
        self.context: Mapping[Any, Any] | None = None

    async def get(self, dependency_key: type[T] | str) -> Any:
        """Resolve a dependency or raise ``KeyError`` when absent."""
        return self._provides[dependency_key]

get(dependency_key) async

Resolve a dependency or raise KeyError when absent.

Source code in servicewright/testing.py
async def get(self, dependency_key: type[T] | str) -> Any:
    """Resolve a dependency or raise ``KeyError`` when absent."""
    return self._provides[dependency_key]

FakeContainer

In-memory :class:DependencyContainerProtocol for tests and users.

Source code in servicewright/testing.py
class FakeContainer:
    """In-memory :class:`DependencyContainerProtocol` for tests and users."""

    def __init__(self, provides: Mapping[Any, Any] | None = None) -> None:
        self._provides: dict[Any, Any] = dict(provides) if provides else {}
        self.app_scopes_opened = 0
        self.unit_scopes_opened = 0
        self.unit_contexts: list[Mapping[Any, Any] | None] = []

    @contextlib.asynccontextmanager
    async def app_scope(self) -> AsyncIterator[AppScopeProtocol]:
        """Yield a fresh application scope."""
        self.app_scopes_opened += 1
        yield FakeScope(self._provides)

    @contextlib.asynccontextmanager
    async def unit_scope(self, context: Mapping[Any, Any] | None = None) -> AsyncIterator[UnitScopeProtocol]:
        """Yield a fresh unit scope carrying ``context``."""
        self.unit_scopes_opened += 1
        self.unit_contexts.append(context)
        scope = FakeScope(self._provides)
        scope.context = context
        yield scope

app_scope() async

Yield a fresh application scope.

Source code in servicewright/testing.py
@contextlib.asynccontextmanager
async def app_scope(self) -> AsyncIterator[AppScopeProtocol]:
    """Yield a fresh application scope."""
    self.app_scopes_opened += 1
    yield FakeScope(self._provides)

unit_scope(context=None) async

Yield a fresh unit scope carrying context.

Source code in servicewright/testing.py
@contextlib.asynccontextmanager
async def unit_scope(self, context: Mapping[Any, Any] | None = None) -> AsyncIterator[UnitScopeProtocol]:
    """Yield a fresh unit scope carrying ``context``."""
    self.unit_scopes_opened += 1
    self.unit_contexts.append(context)
    scope = FakeScope(self._provides)
    scope.context = context
    yield scope

FakeEntrypoint

Bases: ScopedEntrypoint

Recording entrypoint that serves until stop is set.

Useful in tests to assert lifecycle ordering. Set run_once=True to return immediately from serve (e.g. to model an essential exit).

Source code in servicewright/testing.py
class FakeEntrypoint(ScopedEntrypoint):
    """Recording entrypoint that serves until ``stop`` is set.

    Useful in tests to assert lifecycle ordering. Set ``run_once=True`` to
    return immediately from ``serve`` (e.g. to model an essential exit).
    """

    def __init__(
        self,
        *,
        kind: str = "fake",
        essential: bool = True,
        run_once: bool = False,
    ) -> None:
        super().__init__()
        self.kind = kind
        self.essential = essential
        self._run_once = run_once
        self.events: list[str] = []

    async def bind(self, ctx: ServiceContext) -> None:
        await super().bind(ctx)
        self.events.append("bind")

    async def serve(self, *, stop: asyncio.Event) -> None:
        self.events.append("serve")
        if self._run_once:
            return
        await stop.wait()

    async def drain(self, grace: float) -> None:
        self.events.append("drain")

    async def stop(self) -> None:
        self.events.append("stop")

unit_scope(context=None)

Open a per-unit-of-work DI scope.

Raises:

Type Description
RuntimeError

If called before :meth:bind.

Source code in servicewright/core/contracts/bases.py
def unit_scope(
    self, context: Mapping[Any, Any] | None = None
) -> contextlib.AbstractAsyncContextManager[UnitScopeProtocol]:
    """Open a per-unit-of-work DI scope.

    Raises:
        RuntimeError: If called before :meth:`bind`.
    """
    if self._container is None:
        raise RuntimeError("unit_scope() called before bind(); entrypoint is not bound to a container")
    return self._container.unit_scope(context)