mcous / decoy

🦆 Opinionated mocking library for Python
https://michael.cousins.io/decoy/
MIT License
26 stars 1 forks source link

Feature: context manager mocking #58

Closed mcous closed 2 years ago

mcous commented 3 years ago

Overview

In Python, context managers present a useful and reliable API for performing actions that require cleanup. Often, the right tool for a given job is a context manager.

Decoy doesn't provide a good interface for mocking out a dependency that is a context manager. Sine Decoy's job is to help you design your internal APIs, it should ideally let you shake our a dependency that works best as a context manager.

Ideas and brainstorming

mcous commented 3 years ago

then_enter_with is both pretty difficult to type and unnecessary with the addition of nullcontext to contextlib. Might be better as a docs addition instead of a Stub method to maintain

def test_enter_with_smoke_test(decoy: Decoy) -> None:
    """It should be able to stub out a context manager method."""

    class _ContextManager:
        def get_value(self) -> int:
            raise NotImplementedError()

    class _ContextParent:
        @contextmanager
        def get_context(self) -> Iterator[_ContextManager]:
            raise NotImplementedError()

    context_parent = decoy.mock(cls=_ContextParent)
    context_manager = decoy.mock(cls=_ContextManager)

    decoy.when(context_parent.get_context()).then_return(nullcontext(context_manager))
    decoy.when(context_manager.get_value()).then_return(42)

    with context_parent.get_context() as subject:
        result = subject.get_value()

    assert result == 42

Still to research: __enter__ and __exit__ behavior of spies

mcous commented 3 years ago

Currently, the Spy class is incompatible with usage as a ContextManager (i.e. it'll fail out if you try to decoy.when(xyz.__enter__())). It looks like there's a path forward involving defining __enter__ and __exit__ methods on Spy and leaning on typechecking (rather than runtime checking) to alert the user if usage of with is improper in a given test