def test_warn_if_called_incorrectly() -> None: """It should trigger a warning if bound_args is called incorrectly.""" subject = Spec(source=some_func, name=None) with pytest.warns(IncorrectCallWarning, match="missing a required argument"): subject.bind_args(wrong_arg_name="1")
def test_init() -> None: """It should have default spec properties.""" subject = Spec(source=None, name=None) assert subject.get_signature() is None assert subject.get_class_type() is None assert subject.get_is_async() is False
def test_spy_calls( decoy: Decoy, call_handler: CallHandler, spy_creator: SpyCreator, spec: Spec, ) -> None: """It should send any calls to the call handler.""" subject = Spy(spec=spec, call_handler=call_handler, spy_creator=spy_creator) decoy.when(spec.get_name()).then_return("spy_name") decoy.when(spec.bind_args(1, 2, three=3)).then_return( BoundArgs(args=(1, 2, 3), kwargs={})) decoy.when( call_handler.handle( SpyEvent( spy_id=id(subject), spy_name="spy_name", payload=SpyCall(args=(1, 2, 3), kwargs={}), ))).then_return(CallHandlerResult(42)) result = subject(1, 2, three=3) assert result == 42
def test_spy_prop_delete( decoy: Decoy, call_handler: CallHandler, spy_creator: SpyCreator, spec: Spec, ) -> None: """It should record a property set call.""" decoy.when(spec.get_name()).then_return("spy_name") subject = Spy(spec=spec, call_handler=call_handler, spy_creator=spy_creator) subject.some_property = 42 del subject.some_property assert subject.some_property != 42 decoy.verify( call_handler.handle( SpyEvent( spy_id=id(subject), spy_name="spy_name", payload=SpyPropAccess( prop_name="some_property", access_type=PropAccessType.DELETE, ), ), ))
def test_spy_prop_get( decoy: Decoy, call_handler: CallHandler, spy_creator: SpyCreator, spec: Spec, ) -> None: """It should record a property get call.""" subject = Spy(spec=spec, call_handler=call_handler, spy_creator=spy_creator) decoy.when(spec.get_name()).then_return("spy_name") decoy.when( call_handler.handle( SpyEvent( spy_id=id(subject), spy_name="spy_name", payload=SpyPropAccess( prop_name="some_property", access_type=PropAccessType.GET, ), ), )).then_return(CallHandlerResult(42)) result = subject.some_property assert result == 42
def test_bind_args( subject: Spec, input_args: Tuple[Any, ...], input_kwargs: Dict[str, Any], expected_args: Tuple[Any, ...], expected_kwargs: Dict[str, Any], ) -> None: """It should bind arguments to the spec's callable signature.""" result = subject.bind_args(*input_args, **input_kwargs) assert result == BoundArgs(args=expected_args, kwargs=expected_kwargs)
def test_get_signature_no_type_hints() -> None: """It should gracefully degrade if a class's type hints cannot be resolved.""" class _BadTypeHints: _not_ok: "None[Any]" def _ok(self, hello: str) -> None: ... subject = Spec(source=_BadTypeHints, name=None).get_child_spec("_ok") assert subject.get_signature() == inspect.Signature( parameters=[ inspect.Parameter( name="hello", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=str, ) ], return_annotation=None, )
async def test_spy_async_context_manager( decoy: Decoy, call_handler: CallHandler, spy_creator: SpyCreator, spec: Spec, ) -> None: """It should be usable in an `async with` statement.""" enter_spec = decoy.mock(cls=Spec) exit_spec = decoy.mock(cls=Spec) enter_spy = decoy.mock(cls=AsyncSpy) exit_spy = decoy.mock(cls=AsyncSpy) error = RuntimeError("oh no") decoy.when(spec.get_name()).then_return("spy_name") decoy.when(spec.get_child_spec("__aenter__")).then_return(enter_spec) decoy.when(spec.get_child_spec("__aexit__")).then_return(exit_spec) decoy.when(spy_creator.create(spec=enter_spec, is_async=True)).then_return(enter_spy) decoy.when(spy_creator.create(spec=exit_spec, is_async=True)).then_return(exit_spy) decoy.when(await enter_spy()).then_return(42) subject = Spy(spec=spec, call_handler=call_handler, spy_creator=spy_creator) tb = None try: async with subject as result: assert result == 42 raise error except RuntimeError: tb = sys.exc_info()[2] pass decoy.verify(await exit_spy(RuntimeError, error, tb))
def test_get_name(subject: Spec, expected_name: str, expected_full_name: str) -> None: """It should assign names from args or spec.""" assert subject.get_name() == expected_name assert subject.get_full_name() == expected_full_name
def test_get_is_async(subject: Spec, expected_is_async: bool) -> None: """It should get whether the Spec represents an async callable.""" assert subject.get_is_async() is expected_is_async
def test_get_class_type(subject: Spec, expected_class_type: Type[Any]) -> None: """It should get the class type, if source is a class.""" assert subject.get_class_type() == expected_class_type
def test_get_signature( subject: Spec, expected_signature: Optional[inspect.Signature], ) -> None: """It should inspect the spec source's signature.""" assert subject.get_signature() == expected_signature
assert subject.get_is_async() is False class GetNameSpec(NamedTuple): """Spec data to test get_name and get_full_name.""" subject: Spec expected_name: str expected_full_name: str @pytest.mark.parametrize( GetNameSpec._fields, [ GetNameSpec( subject=Spec(source=None, name=None), expected_name="unnamed", expected_full_name="unnamed", ), GetNameSpec( subject=Spec(source=some_func, name=None), expected_name="some_func", expected_full_name="tests.common.some_func", ), GetNameSpec( subject=Spec( source=some_func, name="spy_name", module_name="module_name"), expected_name="spy_name", expected_full_name="module_name.spy_name", ), GetNameSpec(