def test_get_all() -> None: """It can get a list of 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) assert subject.get_all() == [ WhenRehearsal( spy_id=101, spy_name="spy_1", payload=SpyCall(args=(), kwargs={}), ), SpyEvent( spy_id=101, spy_name="spy_1", payload=SpyCall(args=(), kwargs={}), ), SpyEvent(spy_id=202, spy_name="spy_2", payload=SpyCall(args=(), kwargs={})), ]
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_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_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_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_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_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_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_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_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_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()
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_prop( decoy: Decoy, spy_log: SpyLog, subject: DecoyCore, ) -> None: """It should be able to create set and delete rehearsals.""" rehearsal = PropRehearsal( spy_id=1, spy_name="my_spy", payload=SpyPropAccess(prop_name="my_prop", access_type=PropAccessType.GET), ) decoy.when(spy_log.consume_prop_rehearsal()).then_return(rehearsal) result = subject.prop("__rehearsal__") result.set("hello") expected_set_event = SpyEvent( spy_id=1, spy_name="my_spy", payload=SpyPropAccess( prop_name="my_prop", access_type=PropAccessType.SET, value="hello", ), ) decoy.verify(spy_log.push(expected_set_event), times=1) result.delete() expected_delete_event = SpyEvent( spy_id=1, spy_name="my_spy", payload=SpyPropAccess(prop_name="my_prop", access_type=PropAccessType.DELETE), ) decoy.verify(spy_log.push(expected_delete_event), times=1)
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_push_and_consume_prop_rehearsal_for_prop() -> None: """It should be able to push and consume a prop rehearsal for more rehearsals.""" subject = SpyLog() event = SpyEvent( spy_id=42, spy_name="my_spy", payload=SpyPropAccess(prop_name="my_prop", access_type=PropAccessType.GET), ) subject.push(event) result = subject.consume_prop_rehearsal() assert isinstance(result, PropRehearsal) assert result == event
def test_get_by_call_once_behavior() -> None: """It should consume any behavior marked with the `once` flag.""" subject = StubStore() rehearsal = WhenRehearsal(spy_id=42, spy_name="my_spy", payload=SpyCall(args=(1, 2, 3), kwargs={})) behavior = StubBehavior(return_value="fizzbuzz", once=True) subject.add(rehearsal=rehearsal, behavior=behavior) result = subject.get_by_call( call=SpyEvent(spy_id=42, spy_name="my_spy", payload=SpyCall(args=(1, 2, 3), kwargs={}))) assert result == behavior result = subject.get_by_call( call=SpyEvent(spy_id=42, spy_name="my_spy", payload=SpyCall(args=(1, 2, 3), kwargs={}))) assert result is None
def test_push_and_consume_prop_rehearsal_for_when() -> None: """It should be able to push and consume a prop rehearsal for stubbing.""" subject = SpyLog() event = SpyEvent( spy_id=42, spy_name="my_spy", payload=SpyPropAccess(prop_name="my_prop", access_type=PropAccessType.GET), ) subject.push(event) result = subject.consume_when_rehearsal(ignore_extra_args=False) assert isinstance(result, WhenRehearsal) assert result == event
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_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_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_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_prop_get_with_action( decoy: Decoy, spy_log: SpyLog, stub_store: StubStore, subject: CallHandler, ) -> None: """It should trigger a prop get stub's configured action.""" action = decoy.mock() spy_call = SpyEvent( spy_id=42, spy_name="spy_name", payload=SpyPropAccess(prop_name="prop", access_type=PropAccessType.GET), ) behavior = StubBehavior(action=action) decoy.when(stub_store.get_by_call(spy_call)).then_return(behavior) decoy.when(action()).then_return("hello world") result = subject.handle(spy_call) assert result == CallHandlerResult("hello world")
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))
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=[ WhenRehearsal( spy_id=1, spy_name="spy", payload=SpyCall(args=(), kwargs={})
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={})), ], calls=[ SpyEvent( spy_id=101, spy_name="spy_101", payload=SpyCall(args=(1, 2, 3), kwargs={}), ), SpyEvent( spy_id=101, spy_name="spy_101", payload=SpyCall(args=(4, 5, 6), kwargs={}), ), ], times=None, expected_message=os.linesep.join([ "Expected at least 1 call:", "1.\tmy_spy()", "Found 2 calls:", "1.\tspy_101(1, 2, 3)", "2.\tspy_101(4, 5, 6)",