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_child_spy_caching( decoy: Decoy, call_handler: CallHandler, spy_creator: SpyCreator, ) -> None: """It should create a child spy only once.""" parent_spec = decoy.mock(cls=Spec) child_spec = decoy.mock(cls=Spec) child_spy = decoy.mock(cls=Spy) wrong_spy = decoy.mock(cls=Spy) decoy.when(parent_spec.get_child_spec("child")).then_return(child_spec) decoy.when(spy_creator.create(spec=child_spec, is_async=False)).then_return( child_spy, wrong_spy, ) subject = Spy( spec=parent_spec, call_handler=call_handler, spy_creator=spy_creator, ) assert subject.child is child_spy assert subject.child is child_spy
async def test_async_context_manager_mock(decoy: Decoy) -> None: """It should be able to mock an async context manager.""" class _ValueReader(ContextManager[Any]): async def __aenter__(self) -> "_ValueReader": ... async def __aexit__(self, *args: Any) -> None: ... def get_value(self) -> int: ... value_reader = decoy.mock(cls=_ValueReader) def _handle_enter() -> _ValueReader: decoy.when(value_reader.get_value()).then_return(42) return value_reader def _handle_exit(*args: Any) -> None: decoy.when(value_reader.get_value()).then_raise( AssertionError("Context manager exited")) decoy.when(await value_reader.__aenter__()).then_do(_handle_enter) decoy.when(await value_reader.__aexit__(None, None, None)).then_do(_handle_exit) async with value_reader as subject: result = subject.get_value() assert result == 42 with pytest.raises(AssertionError, match="exited"): subject.get_value()
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_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_when_then_raise(decoy: Decoy) -> None: """It should be able to configure a stub raise with a rehearsal.""" subject = decoy.mock(func=some_func) decoy.when(subject("goodbye")).then_raise(ValueError("oh no")) with pytest.raises(ValueError, match="oh no"): subject("goodbye")
def test_property_getter_stub_then_raise(decoy: Decoy) -> None: """It should be able to stub a property getter to raise.""" subject = decoy.mock() decoy.when(subject.prop_name).then_raise(ValueError("oh no")) with pytest.raises(ValueError, match="oh no"): subject.prop_name
def test_property_getter_stub_then_return_multiple(decoy: Decoy) -> None: """It should be able to stub a property getter with multiple return values.""" subject = decoy.mock() decoy.when(subject.prop_name).then_return(43, 44) assert subject.prop_name == 43 assert subject.prop_name == 44 assert subject.prop_name == 44
def test_property_deleter_stub_then_rase(decoy: Decoy) -> None: """It should be able to stub a property deleter to raise.""" subject = decoy.mock() prop_rehearser = decoy.prop(subject.prop_name) decoy.when(prop_rehearser.delete()).then_raise(ValueError("oh no")) with pytest.raises(ValueError, match="oh no"): del subject.prop_name
def test_property_getter_stub_reconfigure(decoy: Decoy) -> None: """It should be able to reconfigure a property getter.""" subject = decoy.mock() decoy.when(subject.prop_name).then_return(42) assert subject.prop_name == 42 decoy.when(subject.prop_name).then_return(43) assert subject.prop_name == 43
def test_property_getter_stub_then_do(decoy: Decoy) -> None: """It should be able to stub a property getter to act.""" def _handle_get(*args: Any, **kwargs: Any) -> int: return 84 subject = decoy.mock() decoy.when(subject.prop_name).then_do(_handle_get) assert subject.prop_name == 84
def test_create_async_spy(decoy: Decoy, call_handler: CallHandler) -> None: """It should get default configurations from the spec.""" spec = decoy.mock(cls=Spec) decoy.when(spec.get_is_async()).then_return(True) subject = SpyCreator(call_handler=call_handler) result = subject.create(spec=spec) assert isinstance(result, AsyncSpy)
def test_when_ignore_extra_args(decoy: Decoy) -> None: """It should be able to ignore extra args in a stub rehearsal.""" def _get_a_thing(id: str, default: Optional[int] = None) -> int: raise NotImplementedError("intentionally unimplemented") subject = decoy.mock(func=_get_a_thing) decoy.when(subject("some-id"), ignore_extra_args=True).then_return(42) result = subject("some-id", 101) assert result == 42
async def test_when_async(decoy: Decoy) -> None: """It should be able to stub an async method.""" subject = decoy.mock(cls=SomeAsyncClass) decoy.when(await subject.foo("hello")).then_return("world") decoy.when(await subject.bar(0, 1.0, "2")).then_raise(ValueError("oh no")) assert await subject.foo("hello") == "world" with pytest.raises(ValueError, match="oh no"): await subject.bar(0, 1.0, "2")
def test_property_setter_stub_then_raise(decoy: Decoy) -> None: """It should be able to stub a property setter to raise.""" subject = decoy.mock() prop_rehearser = decoy.prop(subject.prop_name) decoy.when(prop_rehearser.set(42)).then_raise(ValueError("oh no")) subject.prop_name = 41 assert subject.prop_name == 41 with pytest.raises(ValueError, match="oh no"): subject.prop_name = 42
def test_when_then_return(decoy: Decoy) -> None: """It should be able to configure a stub return with a rehearsal.""" subject = decoy.mock(func=some_func) decoy.when(subject("hello")).then_return("hello world") result = subject(val="hello") assert result == "hello world" result = subject(val="hello") assert result == "hello world" result = subject("asdfghjkl") assert result is None
def test_when_then_do(decoy: Decoy) -> None: """It should be able to configure a stub action with a rehearsal.""" subject = decoy.mock(func=some_func) action_result = None def _then_do_action(arg: str) -> str: nonlocal action_result action_result = arg return "hello from the other side" decoy.when(subject("what's up")).then_do(_then_do_action) result = subject("what's up") assert action_result == "what's up" assert result == "hello from the other side"
def test_mock_with_name( decoy: Decoy, spy_creator: SpyCreator, call_handler: CallHandler, subject: DecoyCore, ) -> None: """It should create a generic spy by default.""" spy = decoy.mock(cls=Spy) decoy.when(spy_creator.create(spec=None, name="my-spy", is_async=False)).then_return(spy) result = subject.mock(name="my-spy") assert result is spy
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_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_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_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))
class DecoyTestCase(unittest.TestCase): """Decoy test case using unittest.""" def setUp(self) -> None: """Set up before each test.""" self.decoy = Decoy() def tearDown(self) -> None: """Clean up after each test.""" self.decoy.reset() def test_when(self) -> None: """Test that self.decoy.when works.""" mock = self.decoy.mock(cls=SomeClass) self.decoy.when(mock.foo("hello")).then_return("world") assert mock.foo("hello") == "world" def test_verify(self) -> None: """Test that self.decoy.verify works.""" mock = self.decoy.mock(cls=SomeClass) mock.foo("hello") self.decoy.verify(mock.foo("hello"))
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_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_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_child_spy( decoy: Decoy, call_handler: CallHandler, spy_creator: SpyCreator, ) -> None: """It should create a child spy.""" parent_spec = decoy.mock(cls=Spec) child_spec = decoy.mock(cls=Spec) child_spy = decoy.mock(cls=Spy) decoy.when(parent_spec.get_child_spec("child")).then_return(child_spec) decoy.when(spy_creator.create(spec=child_spec, is_async=False)).then_return(child_spy) subject = Spy( spec=parent_spec, call_handler=call_handler, spy_creator=spy_creator, ) result = subject.child assert result is child_spy
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")