import inspect
from dobles.class_double import ClassDouble
from dobles.exceptions import ConstructorDoubleError
from dobles.lifecycle import current_space
from dobles.utils import get_target
[docs]
def expect(target):
"""
Prepares a target object for a method call expectation (mock). The name of the method to expect
should be called as a method on the return value of this function::
expect(foo).bar
Accessing the ``bar`` attribute will return an ``Expectation`` which provides additional methods
to configure the mock.
:param object target: The object that will be mocked.
:return: An ``ExpectationTarget`` for the target object.
"""
return ExpectationTarget(target)
[docs]
def expect_constructor(target):
"""
Set an expectation on a ``ClassDouble`` constructor
:param ClassDouble target: The ClassDouble to set the expectation on.
:return: an ``Expectation`` for the __new__ method.
:raise: ``ConstructorDoubleError`` if target is not a ClassDouble.
"""
if not isinstance(target, ClassDouble):
raise ConstructorDoubleError(
"Cannot allow_constructor of {} since it is not a ClassDouble.".format(
target
),
)
return expect(target)._dobles__new__
class ExpectationTarget(object):
"""A wrapper around a target object that creates new expectations on attribute access."""
def __init__(self, target):
"""
:param Union[str, object] target: The object to wrap.
"""
if isinstance(target, str):
target = get_target(target)
self._proxy = current_space().proxy_for(target)
def __getattribute__(self, attr_name):
"""
Returns the value of existing attributes, and returns a new expectation for any attribute
that doesn't yet exist.
:param str attr_name: The name of the attribute to look up.
:return: The existing value or a new ``Expectation``.
:rtype: object, Expectation
"""
__dict__ = object.__getattribute__(self, "__dict__")
if __dict__ and attr_name in __dict__:
return __dict__[attr_name]
caller = inspect.getframeinfo(inspect.currentframe().f_back)
return self._proxy.add_expectation(attr_name, caller)