def test_consume_verify_rehearsals_ignore_extra_args() -> None: """It should be able to pop a slice off the stack, retaining order.""" subject = SpyLog() call_1 = SpyEvent(spy_id=1, spy_name="spy_1", payload=SpyCall(args=(), kwargs={})) call_2 = SpyEvent(spy_id=2, spy_name="spy_2", payload=SpyCall(args=(), kwargs={})) subject.push(call_1) subject.push(call_2) result = subject.consume_verify_rehearsals(count=2, ignore_extra_args=True) assert result == [ VerifyRehearsal( spy_id=1, spy_name="spy_1", payload=SpyCall(args=(), kwargs={}, ignore_extra_args=True), ), VerifyRehearsal( spy_id=2, spy_name="spy_2", payload=SpyCall(args=(), kwargs={}, ignore_extra_args=True), ), ]
def test_verify( decoy: Decoy, spy_log: SpyLog, verifier: Verifier, subject: DecoyCore, ) -> None: """It should be able to verify a call.""" spy_id = 42 rehearsal = VerifyRehearsal(spy_id=spy_id, spy_name="my_spy", payload=SpyCall(args=(), kwargs={})) call = SpyEvent(spy_id=spy_id, spy_name="my_spy", payload=SpyCall(args=(), kwargs={})) decoy.when( spy_log.consume_verify_rehearsals( count=1, ignore_extra_args=False)).then_return([rehearsal]) decoy.when(spy_log.get_calls_to_verify([spy_id])).then_return([call]) subject.verify("__rehearsal__", times=None, ignore_extra_args=False) decoy.verify( verifier.verify(rehearsals=[rehearsal], calls=[call], times=None))
def test_get_by_call() -> None: """It should be able to add a StubBehavior to the store and get it back.""" subject = StubStore() rehearsal = WhenRehearsal(spy_id=42, spy_name="my_spy", payload=SpyCall(args=(), kwargs={})) behavior = StubBehavior(return_value="hello world") subject.add(rehearsal=rehearsal, behavior=behavior) result = subject.get_by_call(call=SpyEvent( spy_id=42, spy_name="my_spy", payload=SpyCall(args=(), kwargs={}))) assert result == behavior
def test_consume_verify_rehearsals_ignores_prop_gets() -> None: """It should be able to pop a slice off the stack, retaining order.""" subject = SpyLog() call_1 = SpyEvent(spy_id=101, spy_name="spy_1", payload=SpyCall(args=(1, ), kwargs={})) call_2 = SpyEvent( spy_id=101, spy_name="spy_1", payload=SpyPropAccess(prop_name="child", access_type=PropAccessType.GET), ) call_3 = SpyEvent( spy_id=102, spy_name="spy_1.child", payload=SpyCall(args=(2, ), kwargs={}), ) call_4 = SpyEvent( spy_id=102, spy_name="spy_1.child", payload=SpyPropAccess(prop_name="fizz", access_type=PropAccessType.DELETE), ) subject.push(call_1) subject.push(call_2) subject.push(call_3) subject.push(call_4) result = subject.consume_verify_rehearsals(count=3, ignore_extra_args=False) assert result == [ VerifyRehearsal( spy_id=101, spy_name="spy_1", payload=SpyCall(args=(1, ), kwargs={}), ), VerifyRehearsal( spy_id=102, spy_name="spy_1.child", payload=SpyCall(args=(2, ), kwargs={}), ), VerifyRehearsal( spy_id=102, spy_name="spy_1.child", payload=SpyPropAccess(prop_name="fizz", access_type=PropAccessType.DELETE), ), ]
def test_when_then_return_multiple_values( decoy: Decoy, spy_log: SpyLog, stub_store: StubStore, subject: DecoyCore, ) -> None: """It should add multiple return values to a stub.""" rehearsal = WhenRehearsal(spy_id=1, spy_name="my_spy", payload=SpyCall(args=(), kwargs={})) decoy.when(spy_log.consume_when_rehearsal( ignore_extra_args=False)).then_return(rehearsal) result = subject.when(0, ignore_extra_args=False) result.then_return(42, 43, 44) decoy.verify( stub_store.add( rehearsal=rehearsal, behavior=StubBehavior(return_value=44, once=False), ), stub_store.add( rehearsal=rehearsal, behavior=StubBehavior(return_value=43, once=True), ), stub_store.add( rehearsal=rehearsal, behavior=StubBehavior(return_value=42, once=True), ), )
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_get_by_call_empty() -> None: """It should return None if store is empty.""" subject = StubStore() result = subject.get_by_call(call=SpyEvent( spy_id=42, spy_name="my_spy", payload=SpyCall(args=(), kwargs={}))) assert result is None
def test_get_calls_to_verify_skips_prop_gets() -> None: """It does not return prop getters for verification.""" subject = SpyLog() call_1 = SpyEvent( spy_id=101, spy_name="spy_1", payload=SpyPropAccess(prop_name="child", access_type=PropAccessType.GET), ) call_2 = PropRehearsal( spy_id=101, spy_name="spy_1", payload=SpyPropAccess(prop_name="child", access_type=PropAccessType.GET), ) call_3 = SpyEvent( spy_id=102, spy_name="spy_1.child", payload=SpyCall(args=(2, ), kwargs={}), ) call_4 = SpyEvent( spy_id=102, spy_name="spy_1.child", payload=SpyPropAccess(prop_name="fizz", access_type=PropAccessType.DELETE), ) subject.push(call_1) subject.push(call_2) subject.push(call_3) subject.push(call_4) result = subject.get_calls_to_verify([101, 102]) assert result == [call_3, call_4]
def test_clear() -> None: """It should consume any behavior marked with the `once` flag.""" subject = StubStore() call = SpyEvent(spy_id=42, spy_name="my_spy", payload=SpyCall(args=(), kwargs={})) rehearsal = WhenRehearsal(spy_id=42, spy_name="my_spy", payload=SpyCall(args=(), kwargs={})) behavior = StubBehavior(return_value="fizzbuzz") subject.add(rehearsal=rehearsal, behavior=behavior) subject.clear() result = subject.get_by_call(call=call) assert result is None
def test_get_by_call_prefers_latest() -> None: """It should be prefer later stubs if multiple exist.""" subject = StubStore() rehearsal_1 = WhenRehearsal(spy_id=42, spy_name="my_spy", payload=SpyCall(args=(), kwargs={})) behavior_1 = StubBehavior(return_value="hello") rehearsal_2 = WhenRehearsal(spy_id=42, spy_name="my_spy", payload=SpyCall(args=(), kwargs={})) behavior_2 = StubBehavior(return_value="goodbye") subject.add(rehearsal=rehearsal_1, behavior=behavior_1) subject.add(rehearsal=rehearsal_2, behavior=behavior_2) result = subject.get_by_call(call=SpyEvent( spy_id=42, spy_name="my_spy", payload=SpyCall(args=(), kwargs={}))) assert result == behavior_2
def test_consume_verify_rehearsals_raises_error() -> None: """It should raise an error if the stack has too few members to pop a slice.""" subject = SpyLog() call_1 = SpyEvent(spy_id=1, spy_name="spy_1", payload=SpyCall(args=(), kwargs={})) subject.push(call_1) with pytest.raises(MissingRehearsalError): subject.consume_verify_rehearsals(count=2, ignore_extra_args=False)
def test_clear() -> None: """It can clear all calls and rehearsals.""" subject = SpyLog() call_1 = SpyEvent(spy_id=101, spy_name="spy_1", payload=SpyCall(args=(), kwargs={})) call_2 = SpyEvent(spy_id=101, spy_name="spy_1", payload=SpyCall(args=(), kwargs={})) call_3 = SpyEvent(spy_id=202, spy_name="spy_2", payload=SpyCall(args=(), kwargs={})) subject.push(call_1) subject.consume_when_rehearsal(ignore_extra_args=False) subject.push(call_2) subject.push(call_3) subject.consume_when_rehearsal(ignore_extra_args=False) subject.clear() assert subject.get_all() == []
def test_verify_multiple_calls( decoy: Decoy, spy_log: SpyLog, verifier: Verifier, subject: DecoyCore, ) -> None: """It should be able to verify a call.""" spy_id_1 = 42 spy_id_2 = 9001 rehearsals = [ VerifyRehearsal(spy_id=spy_id_1, spy_name="spy_1", payload=SpyCall(args=(), kwargs={})), VerifyRehearsal(spy_id=spy_id_2, spy_name="spy_2", payload=SpyCall(args=(), kwargs={})), ] calls = [ SpyEvent(spy_id=spy_id_1, spy_name="spy_1", payload=SpyCall(args=(), kwargs={})) ] decoy.when( spy_log.consume_verify_rehearsals( count=2, ignore_extra_args=False)).then_return(rehearsals) decoy.when(spy_log.get_calls_to_verify([spy_id_1, spy_id_2])).then_return(calls) subject.verify( "__rehearsal_1__", "__rehearsal_2__", times=None, ignore_extra_args=False, ) decoy.verify( verifier.verify(rehearsals=rehearsals, calls=calls, times=None))
def test_get_calls_to_verify() -> None: """It can get a list of calls made matching spy IDs of given rehearsals.""" subject = SpyLog() call_1 = SpyEvent( spy_id=101, spy_name="spy_1", payload=SpyCall(args=(1, ), kwargs={}), ) call_2 = SpyEvent( spy_id=101, spy_name="spy_1", payload=SpyCall(args=(2, ), kwargs={}), ) call_3 = SpyEvent( spy_id=202, spy_name="spy_2", payload=SpyCall(args=(1, ), kwargs={}), ) call_4 = SpyEvent( spy_id=101, spy_name="spy_1", payload=SpyCall(args=(1, ), kwargs={}), ) subject.push(call_1) subject.push(call_2) subject.consume_when_rehearsal(ignore_extra_args=False) subject.push(call_3) subject.push(call_4) result = subject.get_calls_to_verify([101]) assert result == [call_1, call_4] result = subject.get_calls_to_verify([101, 202]) assert result == [call_1, call_3, call_4] result = subject.get_calls_to_verify([303]) assert result == []
def test_push_and_consume_when_rehearsal() -> None: """It should be able to push and pop from the stack.""" subject = SpyLog() call = SpyEvent( spy_id=42, spy_name="my_spy", payload=SpyCall(args=(), kwargs={}), ) subject.push(call) result = subject.consume_when_rehearsal(ignore_extra_args=False) assert isinstance(result, WhenRehearsal) assert call == result
def test_get_by_call_no_match(call: SpyEvent) -> None: """It should return a no-op StubBehavior if there are no matching calls.""" subject = StubStore() rehearsal = WhenRehearsal( spy_id=42, spy_name="my_spy", payload=SpyCall( args=("hello", "world"), kwargs={"goodbye": "so long"}, ), ) behavior = StubBehavior(return_value="fizzbuzz") subject.add(rehearsal=rehearsal, behavior=behavior) result = subject.get_by_call(call=call) assert result is None
def test_consume_when_rehearsal_raises_empty_error() -> None: """It should raise an error if the stack is empty on pop.""" subject = SpyLog() with pytest.raises(MissingRehearsalError): subject.consume_when_rehearsal(ignore_extra_args=False) call = SpyEvent( spy_id=42, spy_name="my_spy", payload=SpyCall(args=(), kwargs={}), ) subject.push(call) subject.consume_when_rehearsal(ignore_extra_args=False) with pytest.raises(MissingRehearsalError): subject.consume_when_rehearsal(ignore_extra_args=False)
def test_handle_call_with_raise( decoy: Decoy, spy_log: SpyLog, stub_store: StubStore, subject: CallHandler, ) -> None: """It raise a Stub's configured error.""" spy_call = SpyEvent(spy_id=42, spy_name="spy_name", payload=SpyCall(args=(), kwargs={})) behavior = StubBehavior(error=RuntimeError("oh no")) decoy.when(stub_store.get_by_call(spy_call)).then_return(behavior) with pytest.raises(RuntimeError, match="oh no"): subject.handle(spy_call) decoy.verify(spy_log.push(spy_call))
def test_handle_call_with_return( decoy: Decoy, spy_log: SpyLog, stub_store: StubStore, subject: CallHandler, ) -> None: """It return a Stub's configured return value.""" spy_call = SpyEvent(spy_id=42, spy_name="spy_name", payload=SpyCall(args=(), kwargs={})) behavior = StubBehavior(return_value="hello world") decoy.when(stub_store.get_by_call(spy_call)).then_return(behavior) result = subject.handle(spy_call) assert result == CallHandlerResult("hello world") decoy.verify(spy_log.push(spy_call))
def test_handle_call_with_no_stubbing( decoy: Decoy, spy_log: SpyLog, stub_store: StubStore, subject: CallHandler, ) -> None: """It should noop and add the call to the stack if no stubbing is configured.""" spy_call = SpyEvent(spy_id=42, spy_name="spy_name", payload=SpyCall(args=(), kwargs={})) behavior = None decoy.when(stub_store.get_by_call(spy_call)).then_return(behavior) result = subject.handle(spy_call) assert result is None decoy.verify(spy_log.push(spy_call))
def test_handle_call_with_context_enter( decoy: Decoy, spy_log: SpyLog, stub_store: StubStore, subject: CallHandler, ) -> None: """It return a Stub's configured context value.""" spy_call = SpyEvent(spy_id=42, spy_name="spy_name", payload=SpyCall(args=(), kwargs={})) behavior = StubBehavior(context_value="hello world") decoy.when(stub_store.get_by_call(spy_call)).then_return(behavior) with subject.handle(spy_call).value as result: # type: ignore[union-attr] assert result == "hello world" decoy.verify(spy_log.push(spy_call))
def test_reset( decoy: Decoy, spy_log: SpyLog, stub_store: StubStore, warning_checker: WarningChecker, subject: DecoyCore, ) -> None: """It should reset the stores.""" call = SpyEvent(spy_id=1, spy_name="my_spy", payload=SpyCall(args=(), kwargs={})) decoy.when(spy_log.get_all()).then_return([call]) subject.reset() decoy.verify( warning_checker.check([call]), spy_log.clear(), stub_store.clear(), )
def test_handle_call_with_action( decoy: Decoy, spy_log: SpyLog, stub_store: StubStore, subject: CallHandler, ) -> None: """It should trigger a stub's configured action.""" action = decoy.mock() spy_call = SpyEvent( spy_id=42, spy_name="spy_name", payload=SpyCall(args=(1, ), kwargs={"foo": "bar"}), ) behavior = StubBehavior(action=action) decoy.when(stub_store.get_by_call(spy_call)).then_return(behavior) decoy.when(action(1, foo="bar")).then_return("hello world") result = subject.handle(spy_call) assert result == CallHandlerResult("hello world")
def test_when_ignore_extra_args( decoy: Decoy, spy_log: SpyLog, stub_store: StubStore, subject: DecoyCore, ) -> None: """It should be able to register a new stubbing.""" rehearsal = WhenRehearsal(spy_id=1, spy_name="my_spy", payload=SpyCall(args=(), kwargs={})) decoy.when(spy_log.consume_when_rehearsal( ignore_extra_args=True)).then_return(rehearsal) result = subject.when("__rehearsal__", ignore_extra_args=True) result.then_return("hello") decoy.verify( stub_store.add( rehearsal=rehearsal, behavior=StubBehavior(return_value="hello", once=False), ))
def test_when_then_raise( decoy: Decoy, spy_log: SpyLog, stub_store: StubStore, subject: DecoyCore, ) -> None: """It should add a raise behavior to a stub.""" rehearsal = WhenRehearsal(spy_id=1, spy_name="my_spy", payload=SpyCall(args=(), kwargs={})) decoy.when(spy_log.consume_when_rehearsal( ignore_extra_args=False)).then_return(rehearsal) error = RuntimeError("oh no") result = subject.when("__rehearsal__", ignore_extra_args=False) result.then_raise(error) decoy.verify( stub_store.add( rehearsal=rehearsal, behavior=StubBehavior(error=error), ))
def test_when_then_do( decoy: Decoy, spy_log: SpyLog, stub_store: StubStore, subject: DecoyCore, ) -> None: """It should add an action behavior to a stub.""" rehearsal = WhenRehearsal(spy_id=1, spy_name="my_spy", payload=SpyCall(args=(), kwargs={})) decoy.when(spy_log.consume_when_rehearsal( ignore_extra_args=False)).then_return(rehearsal) action = lambda: "hello world" # noqa: E731 result = subject.when("__rehearsal__", ignore_extra_args=False) result.then_do(action) decoy.verify( stub_store.add( rehearsal=rehearsal, behavior=StubBehavior(action=action), ))
def test_consume_prop_rehearsal_raises_empty_error() -> None: """It should raise an error a valid rehearsal event isn't found.""" subject = SpyLog() with pytest.raises(MissingRehearsalError): subject.consume_prop_rehearsal() event = SpyEvent( spy_id=42, spy_name="my_spy", payload=SpyPropAccess(prop_name="my_prop", access_type=PropAccessType.GET), ) subject.push(event) subject.consume_prop_rehearsal() with pytest.raises(MissingRehearsalError): subject.consume_prop_rehearsal() call = SpyEvent( spy_id=42, spy_name="my_spy", payload=SpyCall(args=(), kwargs={}), ) subject.push(call) with pytest.raises(MissingRehearsalError): subject.consume_prop_rehearsal() event = SpyEvent( spy_id=42, spy_name="my_spy", payload=SpyPropAccess(prop_name="my_prop", access_type=PropAccessType.DELETE), ) subject.push(event) with pytest.raises(MissingRehearsalError): subject.consume_prop_rehearsal()
class VerifyErrorSpec(NamedTuple): """Spec data for VerifyError tests.""" rehearsals: List[VerifyRehearsal] calls: List[SpyEvent] times: Optional[int] expected_message: str verify_error_specs = [ VerifyErrorSpec( rehearsals=[ VerifyRehearsal(spy_id=42, spy_name="my_spy", payload=SpyCall(args=(), kwargs={})), ], calls=[], times=None, expected_message=os.linesep.join([ "Expected at least 1 call:", "1.\tmy_spy()", "Found 0 calls.", ]), ), VerifyErrorSpec( rehearsals=[ VerifyRehearsal(spy_id=42, spy_name="my_spy", payload=SpyCall(args=(), kwargs={})), ],
all_calls: Sequence[AnySpyEvent] expected_warnings: Sequence[DecoyWarning] warning_checker_specs = [ # it should not warn if there are no calls WarningCheckerSpec( all_calls=[], expected_warnings=[], ), # it should not warn if rehearsals and calls match WarningCheckerSpec( all_calls=[ WhenRehearsal( spy_id=1, spy_name="spy", payload=SpyCall(args=(1,), kwargs={}) ), SpyEvent(spy_id=1, spy_name="spy", payload=SpyCall(args=(1,), kwargs={})), ], expected_warnings=[], ), # it should not warn if a call is made and there are no rehearsals WarningCheckerSpec( all_calls=[ SpyEvent(spy_id=1, spy_name="spy", payload=SpyCall(args=(1,), kwargs={})), ], expected_warnings=[], ), # it should warn if a spy has a rehearsal and a call that doesn't match WarningCheckerSpec( all_calls=[
class MatchEventSpec(NamedTuple): """Spec data for testing `match_event`.""" event: SpyEvent rehearsal: SpyRehearsal expected_result: bool match_event_specs: List[MatchEventSpec] = [ MatchEventSpec( event=SpyEvent( spy_id=42, spy_name="my_spy", payload=SpyCall(args=(), kwargs={}), ), rehearsal=WhenRehearsal( spy_id=42, spy_name="my_spy", payload=SpyCall(args=(), kwargs={}), ), expected_result=True, ), MatchEventSpec( event=SpyEvent( spy_id=42, spy_name="my_spy", payload=SpyCall(args=(1, 2), kwargs={ "foo": "bar", "baz": "qux"