API reference
Main entry point into the flexmock API.
This function is used to either generate a new fake object or take
an existing object (or class or module) and use it as a basis for
a partial mock. In case of a partial mock, the passed in object
is modified to support basic Mock class functionality making
it unnecessary to make successive flexmock()
calls on the same
objects to generate new expectations.
It is safe to call flexmock()
on the same object again, flexmock will
detect when an object has already been partially mocked and return it each
time.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
spec |
Optional[Any] |
Object, class, or module to mock. |
None |
**kwargs |
Any |
Method or return value pairs to attach to the object. |
{} |
Returns:
Type | Description |
---|---|
Mock |
Mock object if no spec is provided. Otherwise return the spec object. |
Examples:
>>> mock = flexmock(Plane)
>>> mock.should_receive("fly")
<flexmock._api.Expectation object at ...>
Source code in flexmock/_api.py
def flexmock(spec: Optional[Any] = None, **kwargs: Any) -> Mock:
"""Main entry point into the flexmock API.
This function is used to either generate a new fake object or take
an existing object (or class or module) and use it as a basis for
a partial mock. In case of a partial mock, the passed in object
is modified to support basic Mock class functionality making
it unnecessary to make successive `flexmock()` calls on the same
objects to generate new expectations.
It is safe to call `flexmock()` on the same object again, flexmock will
detect when an object has already been partially mocked and return it each
time.
Args:
spec: Object, class, or module to mock.
**kwargs: Method or return value pairs to attach to the object.
Returns:
Mock object if no spec is provided. Otherwise return the spec object.
Examples:
>>> mock = flexmock(Plane)
>>> mock.should_receive("fly")
<flexmock._api.Expectation object at ...>
"""
if spec is not None:
return _create_partial_mock(spec, **kwargs)
# use this intermediate class to attach properties
klass = type("MockClass", (Mock,), {})
return klass(**kwargs) # type: ignore
Fake object class returned by the flexmock()
function.
Source code in flexmock/_api.py
class Mock:
"""Fake object class returned by the `flexmock()` function."""
def __init__(self, **kwargs: Any) -> None:
"""Mock constructor.
Args:
**kwargs: dict of attribute/value pairs used to initialize the mock
object.
"""
self._object: Any = self
for attr, value in kwargs.items():
if isinstance(value, property):
setattr(self.__class__, attr, value)
else:
setattr(self, attr, value)
def __enter__(self) -> Any:
return self._object
def __exit__(
self,
exc_type: Optional[Type[BaseException]],
exc_value: Optional[BaseException],
traceback: Optional[TracebackType],
) -> None:
pass
def __call__(self, *args: Any, **kwargs: Any) -> "Mock":
"""Make mocks callable with and without parentheses.
If `Mock` is not callable, it is difficult to mock attributes that
should work with and without parentheses.
"""
return self
def __iter__(self) -> Iterator[Any]:
"""Makes the mock object iterable.
Call the instance's version of __iter__ if available, otherwise yield self.
"""
if (
hasattr(self, "__dict__")
and isinstance(self.__dict__, dict)
and "__iter__" in self.__dict__
):
yield from self.__dict__["__iter__"](self)
else:
yield self
def should_receive(self, name: str) -> "Expectation":
"""Replaces the specified attribute with a fake.
Args:
name: Name of the attribute to replace.
Returns:
Expectation object which can be used to modify the expectations
on the fake attribute.
Examples:
>>> flexmock(plane).should_receive("fly").and_return("vooosh!").once()
<flexmock._api.Expectation object at ...>
>>> plane.fly()
'vooosh!'
"""
if name in UPDATED_ATTRS:
raise FlexmockError("unable to replace flexmock methods")
chained_methods = None
if "." in name:
name, chained_methods = name.split(".", 1)
name = self._update_name_if_mangled(name)
self._ensure_object_has_named_attribute(name)
if chained_methods:
if not isinstance(self._object, Mock) and not hasattr(
getattr(self._object, name), "__call__"
):
# Create a partial mock if the given name is callable
# this allows chaining attributes
return_value = _create_partial_mock(getattr(self._object, name))
else:
return_value = Mock()
self._create_expectation(name, return_value)
return return_value.should_receive(chained_methods)
return self._create_expectation(name)
def _update_name_if_mangled(self, name: str) -> str:
"""This allows flexmock to mock methods with name mangling."""
if name.startswith("__") and not name.endswith("__") and not inspect.ismodule(self._object):
class_name: str
if inspect.isclass(self._object):
class_name = self._object.__name__
else:
class_name = self._object.__class__.__name__
name = f"_{class_name.lstrip('_')}__{name.lstrip('_')}"
return name
def _ensure_object_has_named_attribute(self, name: str) -> None:
if not isinstance(self._object, Mock) and not self._hasattr(self._object, name):
if hasattr(self._object, "__name__"):
obj_name = self._object.__name__
else:
obj_name = str(self._object)
raise FlexmockError(f"{obj_name} does not have attribute '{name}'")
def _hasattr(self, obj: Any, name: str) -> bool:
"""Ensure hasattr checks don't create side-effects for properties."""
if not inspect.isclass(obj) and hasattr(obj, "__dict__") and name not in obj.__dict__:
if name in DEFAULT_CLASS_ATTRIBUTES:
return False # avoid false positives for things like __call__
return hasattr(obj.__class__, name)
return hasattr(obj, name)
def should_call(self, name: str) -> "Expectation":
"""Creates a spy.
This means that the original method will be called rather than the fake
version. However, we can still keep track of how many times it is called
and with what arguments, and apply expectations accordingly.
`should_call` is meaningless/not allowed for non-callable attributes.
Args:
name: Name of the method to spy.
Returns:
Expectation object.
Examples:
>>> flexmock(plane).should_call("repair").once()
<flexmock._api.Expectation object at ...>
>>> plane.repair("wing")
"""
if isinstance(self._object, Mock) and not hasattr(self._object, name):
raise FlexmockError(
f"Mock object does not have attribute '{name}'. "
f'Did you mean to call should_receive("{name}") instead?'
)
expectation = self.should_receive(name)
return expectation.replace_with(expectation.__dict__["_original"])
def new_instances(self, *args: Any) -> "Expectation":
"""Overrides `__new__` method on a class to return custom objects.
Alias for `should_receive('__new__').and_return(args).one_by_one()`.
Args:
*args: Objects to return on each successive call to `__new__`.
Returns:
Expectation object.
Examples:
>>> fake_plane = flexmock(model="fake")
>>> flexmock(Plane).new_instances(fake_plane)
<flexmock._api.Expectation object at ...>
>>> Plane().model
'fake'
It is also possible to return different fake objects in a sequence:
>>> fake_plane1 = flexmock(model="fake1")
>>> fake_plane2 = flexmock(model="fake2")
>>> flexmock(Plane).new_instances(fake_plane1, fake_plane2)
<flexmock._api.Expectation object at ...>
>>> Plane().model
'fake1'
>>> Plane().model
'fake2'
"""
if inspect.isclass(self._object):
return self.should_receive("__new__").and_return(args).one_by_one()
raise FlexmockError("new_instances can only be called on a class mock")
def _create_expectation(self, name: str, return_value: Optional[Any] = None) -> "Expectation":
expectation = self._get_or_create_expectation(name, return_value)
FlexmockContainer.add_expectation(self, expectation)
if _isproperty(self._object, name):
self._update_property(expectation, name)
elif (
isinstance(self._object, Mock)
or hasattr(getattr(self._object, name), "__call__")
or inspect.isclass(getattr(self._object, name))
):
self._update_method(expectation, name)
else:
self._update_attribute(expectation, name, return_value)
return expectation
def _get_or_create_expectation(
self, name: str, return_value: Optional[Any] = None
) -> "Expectation":
saved_expectations = FlexmockContainer.get_expectations_with_name(self, name)
if saved_expectations:
# If there is already an expectation for the same name, get the
# original object from the FIRST saved expectation.
return Expectation(
self._object,
name=name,
return_value=return_value,
original=saved_expectations[0].__dict__.get("_original"),
method_type=saved_expectations[0].__dict__.get("_method_type"),
)
return Expectation(self._object, name=name, return_value=return_value)
def _create_placeholder_mock_for_proper_teardown(
self, obj: Any, name: str, original: Any
) -> None:
"""Ensures that the given function is replaced on teardown."""
mock = Mock()
mock._object = obj
expectation = Expectation(obj, name=name, original=original)
FlexmockContainer.add_expectation(mock, expectation)
def _update_method(self, expectation: "Expectation", name: str) -> None:
method_instance = self._create_mock_method(name)
if self._hasattr(self._object, name) and not hasattr(expectation, "_original"):
expectation._update_original(name, self._object)
expectation._method_type = self._get_method_type(name, expectation._original)
if expectation._method_type in SPECIAL_METHODS:
expectation._original_function = getattr(self._object, name)
if not inspect.isclass(self._object) or expectation._method_type in SPECIAL_METHODS:
method_instance = types.MethodType(method_instance, self._object)
expectation._local_override = _setattr(self._object, name, method_instance)
if (
expectation._local_override
and not inspect.isclass(self._object)
and not isinstance(self._object, Mock)
and hasattr(self._object.__class__, name)
):
self._update_class_for_magic_builtins(name)
def _get_method_type(self, name: str, method: Callable[..., Any]) -> Any:
"""Get method type of the original method.
Method type is saved because after mocking the base class, it is difficult to determine
the original method type.
"""
method_type = self._get_saved_method_type(name, method)
if method_type is not None:
return method_type
if _is_class_method(method, name):
method_type = classmethod
elif _is_static_method(self._object, name):
method_type = staticmethod
else:
method_type = type(method)
setattr(self._object, f"{name}__flexmock__method_type", method_type)
return method_type
def _get_saved_method_type(self, name: str, method: Callable[..., Any]) -> Optional[Any]:
"""Check method type of the original method if it was saved to the class or base class."""
bound_to = getattr(method, "__self__", None)
if bound_to is not None and inspect.isclass(bound_to):
# Check if the method type was saved in a base class
for cls in inspect.getmro(bound_to):
method_type = vars(cls).get(f"{name}__flexmock__method_type")
if method_type:
return method_type
return None
def _update_class_for_magic_builtins(self, name: str) -> None:
"""Fixes method resolution order for built-in methods.
Replacing magic builtins on instances has no effect as the one attached
to the class takes precedence. To work around it, we update the class'
method to check if the instance in question has one in its own __dict__
and call that instead.
"""
if not (name.startswith("__") and name.endswith("__") and len(name) > 4):
return
original = getattr(self._object.__class__, name)
def updated(self: Any, *kargs: Any, **kwargs: Any) -> Any:
if (
hasattr(self, "__dict__")
and isinstance(self.__dict__, dict)
and name in self.__dict__
):
return self.__dict__[name](*kargs, **kwargs)
return original(self, *kargs, **kwargs)
setattr(self._object.__class__, name, updated)
if updated.__code__ != original.__code__:
self._create_placeholder_mock_for_proper_teardown(
self._object.__class__, name, original
)
def _update_attribute(
self, expectation: "Expectation", name: str, return_value: Optional[Any] = None
) -> None:
expectation._callable = False
if self._hasattr(self._object, name) and not hasattr(expectation, "_original"):
expectation._update_original(name, self._object)
expectation._local_override = _setattr(self._object, name, return_value)
def _update_property(self, expectation: "Expectation", name: str) -> None:
new_name = f"_flexmock__{name}"
obj = self._object
if not inspect.isclass(obj):
obj = obj.__class__
expectation._callable = False
original = getattr(obj, name)
@property # type: ignore
def updated(self: Any) -> Any:
if (
hasattr(self, "__dict__")
and isinstance(self.__dict__, dict)
and name in self.__dict__
):
return self.__dict__[name]
# Return original for instances that are not mocked
return getattr(self, new_name)
setattr(obj, name, updated)
if not hasattr(obj, new_name):
# don't try to double update
FlexmockContainer.add_teardown_property(obj, new_name)
setattr(obj, new_name, original)
self._create_placeholder_mock_for_proper_teardown(obj, name, original)
def _create_mock_method(self, name: str) -> Callable[..., Any]:
def _handle_exception_matching(expectation: Expectation) -> None:
# pylint: disable=misplaced-bare-raise
return_values = expectation._return_values
if return_values:
raised, instance = sys.exc_info()[:2]
assert raised, "no exception was raised"
message = str(instance)
expected = return_values[0].raises
if not expected:
raise
args = return_values[0].value
if inspect.isclass(expected):
assert isinstance(args, dict)
expected_instance = expected(*args["kargs"], **args["kwargs"])
expected_message = str(expected_instance)
if expected is not raised and expected not in raised.__bases__:
raise ExceptionClassError(
f"Raised exception for call {expectation._name} "
"did not match expectation:\n"
f" Expected:\t{expected}\n"
f" Raised:\t{raised}"
)
if args["kargs"] and isinstance(args["kargs"][0], RE_TYPE):
if not args["kargs"][0].search(message):
raise ExceptionMessageError(
f"Error message mismatch with raised {expected.__name__}:\n"
f" Expected pattern:\n\t/{args['kargs'][0].pattern}/\n"
f" Received message:\n\t'{message}'"
)
elif expected_message and expected_message != message:
raise (
ExceptionMessageError(
f"Error message mismatch with raised {expected.__name__}:\n"
f" Expected message:\n\t'{message}'\n"
f" Received message:\n\t'{expected_message}'"
)
)
elif expected is not raised:
raise ExceptionClassError(
f"Raised exception for call {expectation._name} "
f"did not match expectation:\n"
f" Expected:\t{repr(expected)}\n"
f" Raised:\t{raised}\n\n"
"Did you try to call and_raise with an instance?\n"
'Instead of and_raise(Exception("arg")), try and_raise(Exception, "arg")'
)
else:
raise
def match_return_values(expected: Any, received: Any) -> bool:
if not isinstance(expected, tuple):
expected = (expected,)
if not isinstance(received, tuple):
received = (received,)
if len(received) != len(expected):
return False
for i, val in enumerate(received):
if not _arguments_match(val, expected[i]):
return False
return True
def pass_thru(
expectation: Expectation, runtime_self: Any, *kargs: Any, **kwargs: Any
) -> Any:
return_values = None
try:
original = expectation._original
_mock = expectation._mock
if inspect.isclass(_mock):
if expectation._method_type in SPECIAL_METHODS:
original = expectation._original_function
return_values = original(*kargs, **kwargs)
else:
return_values = original(runtime_self, *kargs, **kwargs)
else:
return_values = original(*kargs, **kwargs)
except Exception:
return _handle_exception_matching(expectation)
expected_values = expectation._return_values
if expected_values and not match_return_values(expected_values[0].value, return_values):
expected_value = expected_values[0].value
# Display strings with quotes in the error message
if isinstance(return_values, str):
return_values = repr(return_values)
if isinstance(expected_value, str):
expected_value = repr(expected_value)
raise (
MethodSignatureError(
f"Returned values for call {expectation._name} did not match expectation:\n"
f" Expected:\t{expected_value}\n"
f" Returned:\t{return_values}"
)
)
return return_values
def _handle_matched_expectation(
expectation: Expectation, runtime_self: Any, *kargs: Any, **kwargs: Any
) -> Any:
if not expectation._runnable():
raise StateError(
f"{name} expected to be called when {expectation._get_runnable()} is True"
)
expectation._times_called += 1
expectation._verify(final=False)
_pass_thru = expectation._pass_thru
_replace_with = expectation._replace_with
if _pass_thru:
return pass_thru(expectation, runtime_self, *kargs, **kwargs)
if _replace_with:
return _replace_with(*kargs, **kwargs)
return_values = expectation._return_values
if return_values:
return_value = return_values[0]
del return_values[0]
return_values.append(return_value)
else:
return_value = ReturnValue()
if return_value.raises:
if inspect.isclass(return_value.raises):
args = return_value.value
assert isinstance(args, dict)
raise return_value.raises(*args["kargs"], **args["kwargs"])
raise return_value.raises # pylint: disable=raising-bad-type
return return_value.value
def mock_method(runtime_self: Any, *kargs: Any, **kwargs: Any) -> Any:
arguments = {"kargs": kargs, "kwargs": kwargs}
expectation = FlexmockContainer.get_flexmock_expectation(self, name, arguments)
if expectation:
return _handle_matched_expectation(expectation, runtime_self, *kargs, **kwargs)
# inform the user which expectation(s) for the method were _not_ matched
saved_expectations = reversed(FlexmockContainer.get_expectations_with_name(self, name))
error_msg = (
f"Arguments for call {name} did not match expectations:\n"
f" Received call:\t{_format_args(name, arguments)}\n"
)
if saved_expectations:
error_msg += "\n".join(
f" Expected call[{index}]:\t{_format_args(name, expectation._args)}"
for index, expectation in enumerate(saved_expectations, 1)
)
raise MethodSignatureError(error_msg)
return mock_method
new_instances(self, *args)
Overrides __new__
method on a class to return custom objects.
Alias for should_receive('__new__').and_return(args).one_by_one()
.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
*args |
Any |
Objects to return on each successive call to |
() |
Returns:
Type | Description |
---|---|
Expectation |
Expectation object. |
Examples:
>>> fake_plane = flexmock(model="fake")
>>> flexmock(Plane).new_instances(fake_plane)
<flexmock._api.Expectation object at ...>
>>> Plane().model
'fake'
It is also possible to return different fake objects in a sequence:
>>> fake_plane1 = flexmock(model="fake1")
>>> fake_plane2 = flexmock(model="fake2")
>>> flexmock(Plane).new_instances(fake_plane1, fake_plane2)
<flexmock._api.Expectation object at ...>
>>> Plane().model
'fake1'
>>> Plane().model
'fake2'
Source code in flexmock/_api.py
def new_instances(self, *args: Any) -> "Expectation":
"""Overrides `__new__` method on a class to return custom objects.
Alias for `should_receive('__new__').and_return(args).one_by_one()`.
Args:
*args: Objects to return on each successive call to `__new__`.
Returns:
Expectation object.
Examples:
>>> fake_plane = flexmock(model="fake")
>>> flexmock(Plane).new_instances(fake_plane)
<flexmock._api.Expectation object at ...>
>>> Plane().model
'fake'
It is also possible to return different fake objects in a sequence:
>>> fake_plane1 = flexmock(model="fake1")
>>> fake_plane2 = flexmock(model="fake2")
>>> flexmock(Plane).new_instances(fake_plane1, fake_plane2)
<flexmock._api.Expectation object at ...>
>>> Plane().model
'fake1'
>>> Plane().model
'fake2'
"""
if inspect.isclass(self._object):
return self.should_receive("__new__").and_return(args).one_by_one()
raise FlexmockError("new_instances can only be called on a class mock")
should_call(self, name)
Creates a spy.
This means that the original method will be called rather than the fake version. However, we can still keep track of how many times it is called and with what arguments, and apply expectations accordingly.
should_call
is meaningless/not allowed for non-callable attributes.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
name |
str |
Name of the method to spy. |
required |
Returns:
Type | Description |
---|---|
Expectation |
Expectation object. |
Examples:
>>> flexmock(plane).should_call("repair").once()
<flexmock._api.Expectation object at ...>
>>> plane.repair("wing")
Source code in flexmock/_api.py
def should_call(self, name: str) -> "Expectation":
"""Creates a spy.
This means that the original method will be called rather than the fake
version. However, we can still keep track of how many times it is called
and with what arguments, and apply expectations accordingly.
`should_call` is meaningless/not allowed for non-callable attributes.
Args:
name: Name of the method to spy.
Returns:
Expectation object.
Examples:
>>> flexmock(plane).should_call("repair").once()
<flexmock._api.Expectation object at ...>
>>> plane.repair("wing")
"""
if isinstance(self._object, Mock) and not hasattr(self._object, name):
raise FlexmockError(
f"Mock object does not have attribute '{name}'. "
f'Did you mean to call should_receive("{name}") instead?'
)
expectation = self.should_receive(name)
return expectation.replace_with(expectation.__dict__["_original"])
should_receive(self, name)
Replaces the specified attribute with a fake.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
name |
str |
Name of the attribute to replace. |
required |
Returns:
Type | Description |
---|---|
Expectation |
Expectation object which can be used to modify the expectations on the fake attribute. |
Examples:
>>> flexmock(plane).should_receive("fly").and_return("vooosh!").once()
<flexmock._api.Expectation object at ...>
>>> plane.fly()
'vooosh!'
Source code in flexmock/_api.py
def should_receive(self, name: str) -> "Expectation":
"""Replaces the specified attribute with a fake.
Args:
name: Name of the attribute to replace.
Returns:
Expectation object which can be used to modify the expectations
on the fake attribute.
Examples:
>>> flexmock(plane).should_receive("fly").and_return("vooosh!").once()
<flexmock._api.Expectation object at ...>
>>> plane.fly()
'vooosh!'
"""
if name in UPDATED_ATTRS:
raise FlexmockError("unable to replace flexmock methods")
chained_methods = None
if "." in name:
name, chained_methods = name.split(".", 1)
name = self._update_name_if_mangled(name)
self._ensure_object_has_named_attribute(name)
if chained_methods:
if not isinstance(self._object, Mock) and not hasattr(
getattr(self._object, name), "__call__"
):
# Create a partial mock if the given name is callable
# this allows chaining attributes
return_value = _create_partial_mock(getattr(self._object, name))
else:
return_value = Mock()
self._create_expectation(name, return_value)
return return_value.should_receive(chained_methods)
return self._create_expectation(name)
Holds expectations about methods.
The information contained in the Expectation object includes method name, its argument list, return values, and any exceptions that the method might raise.
Source code in flexmock/_api.py
class Expectation:
"""Holds expectations about methods.
The information contained in the Expectation object includes method name,
its argument list, return values, and any exceptions that the method might
raise.
"""
def __init__(
self,
mock: Mock,
name: Optional[str] = None,
return_value: Optional[Any] = None,
original: Optional[Any] = None,
method_type: Optional[Any] = None,
) -> None:
if original is not None:
self._original = original
self._name = name
self._times_called: int = 0
self._modifier: str = EXACTLY
self._args: Optional[Dict[str, Any]] = None
self._method_type = method_type
self._argspec: Optional[inspect.FullArgSpec] = None
self._return_values = [ReturnValue(return_value)] if return_value is not None else []
self._replace_with: Optional[Callable[..., Any]] = None
self._original_function: Optional[Callable[..., Any]] = None
self._expected_calls: Dict[str, Optional[int]] = {
EXACTLY: None,
AT_LEAST: None,
AT_MOST: None,
}
self._runnable: Callable[..., bool] = lambda: True
self._mock = mock
self._pass_thru = False
self._ordered = False
self._one_by_one = False
self._verified = False
self._callable = True
self._local_override = False
# Remove in 1.0. Issue #96
self._called_deprecated_property = False
def __str__(self) -> str:
args = _format_args(str(self._name), self._args)
return_values = ", ".join(str(x) for x in self._return_values)
return f"{args} -> ({return_values})"
def __getattribute__(self, name: str) -> Any:
# Workaround to raise an error if once, twice, or never are called
# without parenthesis.
if name in ("once", "twice", "never", "mock"):
self._called_deprecated_property = True
return object.__getattribute__(self, name)
def _get_runnable(self) -> str:
"""Ugly hack to get the name of when() condition from the source code."""
name = "condition"
try:
source = inspect.getsource(self._runnable)
if "when(" in source:
name = source.split("when(")[1].split(")")[0]
elif "def " in source:
name = source.split("def ")[1].split("(")[0]
except Exception:
# couldn't get the source, oh well
pass
return name
def _verify_signature_match(self, *kargs: Any, **kwargs: Any) -> None:
if isinstance(self._mock, Mock):
return # no sense in enforcing this for fake objects
allowed = self._argspec
args_len = len(allowed.args)
# self is the first expected argument
has_self = allowed.args and allowed.args[0] == "self"
# Builtin methods take `self` as the first argument but `inspect.ismethod` returns False
# so we need to check for them explicitly
is_builtin_method = isinstance(self._original, BuiltinMethodType) and has_self
# Methods take `self` if not a staticmethod
is_method = inspect.ismethod(self._original) and self._method_type is not staticmethod
# Class init takes `self`
is_class = inspect.isclass(self._original)
# When calling class methods or instance methods on a class method takes `cls`
is_class_method = (
inspect.isfunction(self._original)
and inspect.isclass(self._mock)
and self._method_type is not staticmethod
)
if is_builtin_method or is_method or is_class or is_class_method:
# Do not count `self` or `cls`.
args_len -= 1
minimum = args_len - (allowed.defaults and len(allowed.defaults) or 0)
maximum = None
if allowed.varargs is None and allowed.varkw is None:
maximum = args_len
total_positional = len(kargs + tuple(a for a in kwargs if a in allowed.args))
named_optionals = [
a
for a in kwargs
if allowed.defaults
if a in allowed.args[len(allowed.args) - len(allowed.defaults) :]
]
if allowed.defaults and total_positional == minimum and named_optionals:
minimum += len(named_optionals)
if total_positional < minimum:
arguments = "argument" if minimum == 1 else "arguments"
raise MethodSignatureError(
f"{self._name} requires at least {minimum} {arguments}, "
f"expectation provided {total_positional}"
)
if maximum is not None and total_positional > maximum:
arguments = "argument" if maximum == 1 else "arguments"
raise MethodSignatureError(
f"{self._name} requires at most {maximum} {arguments}, "
f"expectation provided {total_positional}"
)
if args_len == len(kargs) and any(a for a in kwargs if a in allowed.args):
given_args = [a for a in kwargs if a in allowed.args]
arguments = "argument" if len(given_args) == 1 else "arguments"
raise MethodSignatureError(
f"{given_args} already given as positional {arguments} to {self._name}"
)
if not allowed.varkw and any(
a for a in kwargs if a not in allowed.args + allowed.kwonlyargs
):
invalid_arg = [a for a in kwargs if a not in allowed.args + allowed.kwonlyargs][0]
raise MethodSignatureError(
f"{invalid_arg} is not a valid keyword argument to {self._name}"
)
# check that kwonlyargs that don't have default value specified are provided
required_kwonlyargs = [
a for a in allowed.kwonlyargs if a not in (allowed.kwonlydefaults or {})
]
missing_kwonlyargs = [a for a in required_kwonlyargs if a not in kwargs]
if missing_kwonlyargs:
arguments = "argument" if len(missing_kwonlyargs) == 1 else "arguments"
missing_args = '", "'.join(missing_kwonlyargs)
raise MethodSignatureError(
f'{self._name} requires keyword-only {arguments} "{missing_args}"'
)
def _update_original(self, name: str, obj: Any) -> None:
if hasattr(obj, "__dict__") and name in obj.__dict__:
self._original = obj.__dict__[name]
else:
self._original = getattr(obj, name)
self._update_argspec()
def _update_argspec(self) -> None:
original = self.__dict__.get("_original")
if original:
try:
self._argspec = inspect.getfullargspec(original)
except TypeError:
# built-in function: fall back to stupid processing and hope the
# builtins don't change signature
pass
def _normalize_named_args(self, *kargs: Any, **kwargs: Any) -> Dict[str, Any]:
argspec = self._argspec
default = {"kargs": kargs, "kwargs": kwargs}
if not argspec:
return default
ret: Dict[str, Any] = {"kargs": (), "kwargs": kwargs}
if inspect.ismethod(self._original):
args = argspec.args[1:]
else:
args = argspec.args
for i, arg in enumerate(kargs):
if len(args) <= i:
return default
ret["kwargs"][args[i]] = arg
return ret
def __raise(self, exception: Type[BaseException], message: str) -> NoReturn:
"""Safe internal raise implementation.
In case we're patching builtins, it's important to reset the
expectation before raising any exceptions or else things like
open() might be stubbed out and the resulting runner errors are very
difficult to diagnose.
"""
self._reset()
raise exception(message)
def _match_args(self, given_args: Any) -> bool:
"""Check if the set of given arguments matches this expectation."""
expected_args = self._args
given_args = self._normalize_named_args(*given_args["kargs"], **given_args["kwargs"])
if expected_args == given_args or expected_args is None:
return True
if (
len(given_args["kargs"]) != len(expected_args["kargs"])
or len(given_args["kwargs"]) != len(expected_args["kwargs"])
or (sorted(given_args["kwargs"].keys()) != sorted(expected_args["kwargs"].keys()))
):
return False
for i, arg in enumerate(given_args["kargs"]):
if not _arguments_match(arg, expected_args["kargs"][i]):
return False
for key, value in given_args["kwargs"].items():
if not _arguments_match(value, expected_args["kwargs"][key]):
return False
return True
def mock(self) -> Mock:
"""Return the mock associated with this expectation.
Returns:
Mock associated with this expectation.
Examples:
>>> plane = flexmock(plane).should_receive("fly").mock()
"""
self._called_deprecated_property = False
return self._mock
def with_args(self, *args: Any, **kwargs: Any) -> "Expectation":
"""Override the arguments used to match this expectation's method.
Args:
*args: Positional arguments.
**kwargs: Keyword arguments.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
Match calls with no arguments:
>>> flexmock(plane).should_receive("fly").with_args()
<flexmock._api.Expectation object at ...>
Match a single argument:
>>> flexmock(plane).should_receive("fly").with_args("east")
<flexmock._api.Expectation object at ...>
Match keyword arguments:
>>> flexmock(plane).should_receive("fly").with_args("up", destination="Oslo")
<flexmock._api.Expectation object at ...>
Match argument type:
>>> flexmock(plane).should_receive("fly").with_args(str)
<flexmock._api.Expectation object at ...>
Match a string using a compiled regular expression:
>>> regex = re.compile("^(up|down)$")
>>> flexmock(plane).should_receive("fly").with_args(regex)
<flexmock._api.Expectation object at ...>
"""
if not self._callable:
self.__raise(FlexmockError, "can't use with_args() with attribute stubs")
self._update_argspec()
if self._argspec:
# do this outside try block as TypeError is way too general and catches
# unrelated errors in the verify signature code
self._verify_signature_match(*args, **kwargs)
self._args = self._normalize_named_args(*args, **kwargs)
else:
self._args = {"kargs": args, "kwargs": kwargs}
return self
def and_return(self, *values: Any) -> "Expectation":
"""Override the return value of this expectation's method.
When `and_return` is given multiple times, each value provided is returned
on successive invocations of the method. It is also possible to mix
`and_return` with `and_raise` in the same manner to alternate between returning
a value and raising and exception on different method invocations.
When combined with the `one_by_one` modifier, value is treated as a list of
values to be returned in the order specified by successive calls to this
method rather than a single list to be returned each time.
Args:
*values: Optional list of return values, defaults to `None` if not given.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
Provide return values for mocks:
>>> flexmock(plane).should_receive("land").and_return("landed!").once()
<flexmock._api.Expectation object at ...>
>>> plane.land()
'landed!'
Match specific return values with spies:
>>> flexmock(plane).should_call("passenger_count").and_return(3)
<flexmock._api.Expectation object at ...>
>>> plane.passenger_count()
3
"""
if not values:
value = None
elif len(values) == 1:
value = values[0]
else:
value = values
if not self._callable:
_setattr(self._mock, str(self._name), value)
return self
return_values = self._return_values
if not self._one_by_one:
value = ReturnValue(value)
return_values.append(value)
else:
try:
return_values.extend([ReturnValue(v) for v in value]) # type: ignore
except TypeError:
return_values.append(ReturnValue(value))
return self
def times(self, number: int) -> "Expectation":
"""Number of times this expectation's method is expected to be called.
There are also 3 aliases for the `times()` method:
- `once()` -> `times(1)`
- `twice()` -> `times(2)`
- `never()` -> `times(0)`
Args:
number: Expected call count.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("fly").times(1)
<flexmock._api.Expectation object at ...>
>>> plane.fly()
>>> flexmock(plane).should_call("land").times(2)
<flexmock._api.Expectation object at ...>
>>> plane.land()
>>> plane.land()
"""
if not self._callable:
self.__raise(FlexmockError, "can't use times() with attribute stubs")
expected_calls = self._expected_calls
modifier = self._modifier
expected_calls[modifier] = number
return self
def once(self) -> "Expectation":
"""Expect expectation's method to be called once.
Alias for `times(1)`.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("land").once()
<flexmock._api.Expectation object at ...>
>>> plane.land()
"""
self._called_deprecated_property = False
return self.times(1)
def twice(self) -> "Expectation":
"""Expect expectation's method to be called twice.
Alias for `times(2)`.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("fly").twice()
<flexmock._api.Expectation object at ...>
>>> plane.fly()
>>> plane.fly()
"""
self._called_deprecated_property = False
return self.times(2)
def never(self) -> "Expectation":
"""Expect expectation's method to never be called.
Alias for `times(0)`.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("crash").never()
<flexmock._api.Expectation object at ...>
>>> plane.crash()
Traceback (most recent call last):
...
flexmock.exceptions.MethodCallError: crash() expected to be called...
"""
self._called_deprecated_property = False
return self.times(0)
def one_by_one(self) -> "Expectation":
"""Modifies the return value to be treated as a list of return values.
Each value in the list is returned on successive invocations of the method.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("pilot").and_return("pilot1", "pilot2").one_by_one()
<flexmock._api.Expectation object at ...>
>>> plane.pilot()
'pilot1'
>>> plane.pilot()
'pilot2'
"""
if not self._callable:
self.__raise(FlexmockError, "can't use one_by_one() with attribute stubs")
if not self._one_by_one:
self._one_by_one = True
return_values = self._return_values
saved_values = return_values[:]
self._return_values = return_values = []
for value in saved_values:
try:
for val in value.value: # type: ignore
return_values.append(ReturnValue(val))
except TypeError:
return_values.append(value)
return self
def at_least(self) -> "Expectation":
"""Modifies the associated `times()` expectation.
When given, an exception will only be raised if the method is called less
than `times()` specified. Does nothing if `times()` is not given.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("fly").at_least().once()
<flexmock._api.Expectation object at ...>
>>> plane.fly("east")
"""
if not self._callable:
self.__raise(FlexmockError, "can't use at_least() with attribute stubs")
expected_calls = self._expected_calls
modifier = self._modifier
if expected_calls[AT_LEAST] is not None or modifier == AT_LEAST:
self.__raise(FlexmockError, "cannot use at_least modifier twice")
if modifier == AT_MOST and expected_calls[AT_MOST] is None:
self.__raise(FlexmockError, "cannot use at_least with at_most unset")
self._modifier = AT_LEAST
return self
def at_most(self) -> "Expectation":
"""Modifies the associated `times()` expectation.
When given, an exception will only be raised if the method is called more
than `times()` specified. Does nothing if `times()` is not given.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("land").at_most().once()
<flexmock._api.Expectation object at ...>
>>> plane.land()
>>> plane.land()
Traceback (most recent call last):
...
flexmock.exceptions.MethodCallError: land() expected to be called at most...
"""
if not self._callable:
self.__raise(FlexmockError, "can't use at_most() with attribute stubs")
expected_calls = self._expected_calls
modifier = self._modifier
if expected_calls[AT_MOST] is not None or modifier == AT_MOST:
self.__raise(FlexmockError, "cannot use at_most modifier twice")
if modifier == AT_LEAST and expected_calls[AT_LEAST] is None:
self.__raise(FlexmockError, "cannot use at_most with at_least unset")
self._modifier = AT_MOST
return self
def ordered(self) -> "Expectation":
"""Makes the expectation respect the order of `should_receive` statements.
An exception will be raised if methods are called out of order, determined
by order of `should_receive` calls in the test.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("fly").with_args("east").ordered()
<flexmock._api.Expectation object at ...>
>>> flexmock(plane).should_receive("fly").with_args("west").ordered()
<flexmock._api.Expectation object at ...>
>>> plane.fly("west")
Traceback (most recent call last):
...
flexmock.exceptions.CallOrderError: fly("west") called before...
"""
if not self._callable:
self.__raise(FlexmockError, "can't use ordered() with attribute stubs")
self._ordered = True
FlexmockContainer.ordered.append(self)
return self
def when(self, func: Callable[..., Any]) -> "Expectation":
"""Sets an outside resource to be checked when asserting if a method
should be called.
Args:
func: Function that indicates if the mocked or spied method should
have been called.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("land").when(lambda: plane.is_flying)
<flexmock._api.Expectation object at ...>
>>> plane.is_flying
True
>>> plane.land()
>>> plane.is_flying = False
>>> plane.land()
Traceback (most recent call last):
...
flexmock.exceptions.StateError: land expected to be called when...
"""
if not self._callable:
self.__raise(FlexmockError, "can't use when() with attribute stubs")
if not hasattr(func, "__call__"):
self.__raise(FlexmockError, "when() parameter must be callable")
self._runnable = func
return self
def and_raise(self, exception: Type[BaseException], *args: Any, **kwargs: Any) -> "Expectation":
"""Specifies the exception to be raised when this expectation is met.
Args:
exception: Exception class.
*args: Positional arguments to pass to the exception.
**kwargs: Keyword arguments to pass to the exception.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
Make a mocked method raise an exception instead of returning a value:
>>> flexmock(plane).should_receive("fly").and_raise(BadWeatherException)
<flexmock._api.Expectation object at ...>
>>> plane.fly()
Traceback (most recent call last):
...
BadWeatherException
Make a spy to verify that a specific exception is raised:
>>> flexmock(plane).should_call("repair").and_raise(RuntimeError, "err msg")
<flexmock._api.Expectation object at ...>
"""
if not self._callable:
self.__raise(FlexmockError, "can't use and_raise() with attribute stubs")
if inspect.isclass(exception):
try:
exception(*args, **kwargs)
except TypeError:
self.__raise(
FlexmockError, f"can't initialize {exception} with the given arguments"
)
kargs = {"kargs": args, "kwargs": kwargs}
return_values = self._return_values
return_values.append(ReturnValue(raises=exception, value=kargs))
return self
def replace_with(self, function: Callable[..., Any]) -> "Expectation":
"""Gives a function to run instead of the mocked out one.
Args:
function: A callable that is used to replace the original function.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("set_speed").replace_with(lambda x: x == 5)
<flexmock._api.Expectation object at ...>
>>> plane.set_speed(5)
True
>>> plane.set_speed(4)
False
"""
if not self._callable:
self.__raise(FlexmockError, "can't use replace_with() with attribute/property stubs")
replace_with = self._replace_with
original = self.__dict__.get("_original")
if replace_with:
self.__raise(FlexmockError, "replace_with cannot be specified twice")
if function == original:
self._pass_thru = True
self._replace_with = function
return self
def and_yield(self, *args: Any) -> "Expectation":
"""Specifies the list of items to be yielded on successive method calls.
In effect, the mocked object becomes a generator.
Args:
*args: Items to be yielded on successive method calls.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("flight_log").and_yield("fly", "land")
<flexmock._api.Expectation object at ...>
>>> log = plane.flight_log()
>>> next(log)
'fly'
>>> next(log)
'land'
"""
if not self._callable:
self.__raise(FlexmockError, "can't use and_yield() with attribute stubs")
return self.and_return(iter(args))
def _verify(self, final: bool = True) -> None:
"""Verify that this expectation has been met.
Args:
final: True if no further calls to this method expected
(skip checking at_least expectations when False).
Raises:
MethodCallError Exception
"""
failed, message = self._verify_number_of_calls(final)
if failed and not self._verified:
self._verified = True
self.__raise(
MethodCallError,
(
f"{_format_args(str(self._name), self._args)} expected to be called "
f"{message}, called {self._times_called} "
f"{'time' if self._times_called == 1 else 'times'}"
),
)
def _verify_number_of_calls(self, final: bool) -> Tuple[bool, str]:
if self._called_deprecated_property:
raise FlexmockError(
"Calling once, twice, never, or mock without parentheses has been deprecated"
)
failed = False
message = ""
called_exactly = self._expected_calls[EXACTLY]
called_at_least = self._expected_calls[AT_LEAST]
called_at_most = self._expected_calls[AT_MOST]
times_called = self._times_called
if called_exactly is not None:
message = f"exactly {called_exactly}"
if final:
if times_called != called_exactly:
failed = True
else:
if times_called > called_exactly:
failed = True
message += " time" if called_exactly == 1 else " times"
else:
if final and called_at_least is not None:
message = f"at least {called_at_least}"
if times_called < called_at_least:
failed = True
message += " time" if called_at_least == 1 else " times"
if called_at_most is not None:
if message:
message += " and "
message += f"at most {called_at_most}"
if times_called > called_at_most:
failed = True
message += " time" if called_at_most == 1 else " times"
return failed, message
def _reset(self) -> None:
"""Returns the methods overridden by this expectation to their originals."""
_mock = self._mock
if not isinstance(_mock, Mock):
original = self.__dict__.get("_original")
if original:
# name may be unicode but pypy demands dict keys to be str
name = str(self._name)
if hasattr(_mock, "__dict__") and name in _mock.__dict__ and self._local_override:
delattr(_mock, name)
elif (
hasattr(_mock, "__dict__")
and name in _mock.__dict__
and isinstance(_mock.__dict__, dict)
):
_mock.__dict__[name] = original
else:
setattr(_mock, name, original)
del self
and_raise(self, exception, *args, **kwargs)
Specifies the exception to be raised when this expectation is met.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
exception |
Type[BaseException] |
Exception class. |
required |
*args |
Any |
Positional arguments to pass to the exception. |
() |
**kwargs |
Any |
Keyword arguments to pass to the exception. |
{} |
Returns:
Type | Description |
---|---|
Expectation |
Self, i.e. can be chained with other Expectation methods. |
Examples:
Make a mocked method raise an exception instead of returning a value:
>>> flexmock(plane).should_receive("fly").and_raise(BadWeatherException)
<flexmock._api.Expectation object at ...>
>>> plane.fly()
Traceback (most recent call last):
...
BadWeatherException
Make a spy to verify that a specific exception is raised:
>>> flexmock(plane).should_call("repair").and_raise(RuntimeError, "err msg")
<flexmock._api.Expectation object at ...>
Source code in flexmock/_api.py
def and_raise(self, exception: Type[BaseException], *args: Any, **kwargs: Any) -> "Expectation":
"""Specifies the exception to be raised when this expectation is met.
Args:
exception: Exception class.
*args: Positional arguments to pass to the exception.
**kwargs: Keyword arguments to pass to the exception.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
Make a mocked method raise an exception instead of returning a value:
>>> flexmock(plane).should_receive("fly").and_raise(BadWeatherException)
<flexmock._api.Expectation object at ...>
>>> plane.fly()
Traceback (most recent call last):
...
BadWeatherException
Make a spy to verify that a specific exception is raised:
>>> flexmock(plane).should_call("repair").and_raise(RuntimeError, "err msg")
<flexmock._api.Expectation object at ...>
"""
if not self._callable:
self.__raise(FlexmockError, "can't use and_raise() with attribute stubs")
if inspect.isclass(exception):
try:
exception(*args, **kwargs)
except TypeError:
self.__raise(
FlexmockError, f"can't initialize {exception} with the given arguments"
)
kargs = {"kargs": args, "kwargs": kwargs}
return_values = self._return_values
return_values.append(ReturnValue(raises=exception, value=kargs))
return self
and_return(self, *values)
Override the return value of this expectation's method.
When and_return
is given multiple times, each value provided is returned
on successive invocations of the method. It is also possible to mix
and_return
with and_raise
in the same manner to alternate between returning
a value and raising and exception on different method invocations.
When combined with the one_by_one
modifier, value is treated as a list of
values to be returned in the order specified by successive calls to this
method rather than a single list to be returned each time.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
*values |
Any |
Optional list of return values, defaults to |
() |
Returns:
Type | Description |
---|---|
Expectation |
Self, i.e. can be chained with other Expectation methods. |
Examples:
Provide return values for mocks:
>>> flexmock(plane).should_receive("land").and_return("landed!").once()
<flexmock._api.Expectation object at ...>
>>> plane.land()
'landed!'
Match specific return values with spies:
>>> flexmock(plane).should_call("passenger_count").and_return(3)
<flexmock._api.Expectation object at ...>
>>> plane.passenger_count()
3
Source code in flexmock/_api.py
def and_return(self, *values: Any) -> "Expectation":
"""Override the return value of this expectation's method.
When `and_return` is given multiple times, each value provided is returned
on successive invocations of the method. It is also possible to mix
`and_return` with `and_raise` in the same manner to alternate between returning
a value and raising and exception on different method invocations.
When combined with the `one_by_one` modifier, value is treated as a list of
values to be returned in the order specified by successive calls to this
method rather than a single list to be returned each time.
Args:
*values: Optional list of return values, defaults to `None` if not given.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
Provide return values for mocks:
>>> flexmock(plane).should_receive("land").and_return("landed!").once()
<flexmock._api.Expectation object at ...>
>>> plane.land()
'landed!'
Match specific return values with spies:
>>> flexmock(plane).should_call("passenger_count").and_return(3)
<flexmock._api.Expectation object at ...>
>>> plane.passenger_count()
3
"""
if not values:
value = None
elif len(values) == 1:
value = values[0]
else:
value = values
if not self._callable:
_setattr(self._mock, str(self._name), value)
return self
return_values = self._return_values
if not self._one_by_one:
value = ReturnValue(value)
return_values.append(value)
else:
try:
return_values.extend([ReturnValue(v) for v in value]) # type: ignore
except TypeError:
return_values.append(ReturnValue(value))
return self
and_yield(self, *args)
Specifies the list of items to be yielded on successive method calls.
In effect, the mocked object becomes a generator.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
*args |
Any |
Items to be yielded on successive method calls. |
() |
Returns:
Type | Description |
---|---|
Expectation |
Self, i.e. can be chained with other Expectation methods. |
Examples:
>>> flexmock(plane).should_receive("flight_log").and_yield("fly", "land")
<flexmock._api.Expectation object at ...>
>>> log = plane.flight_log()
>>> next(log)
'fly'
>>> next(log)
'land'
Source code in flexmock/_api.py
def and_yield(self, *args: Any) -> "Expectation":
"""Specifies the list of items to be yielded on successive method calls.
In effect, the mocked object becomes a generator.
Args:
*args: Items to be yielded on successive method calls.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("flight_log").and_yield("fly", "land")
<flexmock._api.Expectation object at ...>
>>> log = plane.flight_log()
>>> next(log)
'fly'
>>> next(log)
'land'
"""
if not self._callable:
self.__raise(FlexmockError, "can't use and_yield() with attribute stubs")
return self.and_return(iter(args))
at_least(self)
Modifies the associated times()
expectation.
When given, an exception will only be raised if the method is called less
than times()
specified. Does nothing if times()
is not given.
Returns:
Type | Description |
---|---|
Expectation |
Self, i.e. can be chained with other Expectation methods. |
Examples:
>>> flexmock(plane).should_receive("fly").at_least().once()
<flexmock._api.Expectation object at ...>
>>> plane.fly("east")
Source code in flexmock/_api.py
def at_least(self) -> "Expectation":
"""Modifies the associated `times()` expectation.
When given, an exception will only be raised if the method is called less
than `times()` specified. Does nothing if `times()` is not given.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("fly").at_least().once()
<flexmock._api.Expectation object at ...>
>>> plane.fly("east")
"""
if not self._callable:
self.__raise(FlexmockError, "can't use at_least() with attribute stubs")
expected_calls = self._expected_calls
modifier = self._modifier
if expected_calls[AT_LEAST] is not None or modifier == AT_LEAST:
self.__raise(FlexmockError, "cannot use at_least modifier twice")
if modifier == AT_MOST and expected_calls[AT_MOST] is None:
self.__raise(FlexmockError, "cannot use at_least with at_most unset")
self._modifier = AT_LEAST
return self
at_most(self)
Modifies the associated times()
expectation.
When given, an exception will only be raised if the method is called more
than times()
specified. Does nothing if times()
is not given.
Returns:
Type | Description |
---|---|
Expectation |
Self, i.e. can be chained with other Expectation methods. |
Examples:
>>> flexmock(plane).should_receive("land").at_most().once()
<flexmock._api.Expectation object at ...>
>>> plane.land()
>>> plane.land()
Traceback (most recent call last):
...
flexmock.exceptions.MethodCallError: land() expected to be called at most...
Source code in flexmock/_api.py
def at_most(self) -> "Expectation":
"""Modifies the associated `times()` expectation.
When given, an exception will only be raised if the method is called more
than `times()` specified. Does nothing if `times()` is not given.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("land").at_most().once()
<flexmock._api.Expectation object at ...>
>>> plane.land()
>>> plane.land()
Traceback (most recent call last):
...
flexmock.exceptions.MethodCallError: land() expected to be called at most...
"""
if not self._callable:
self.__raise(FlexmockError, "can't use at_most() with attribute stubs")
expected_calls = self._expected_calls
modifier = self._modifier
if expected_calls[AT_MOST] is not None or modifier == AT_MOST:
self.__raise(FlexmockError, "cannot use at_most modifier twice")
if modifier == AT_LEAST and expected_calls[AT_LEAST] is None:
self.__raise(FlexmockError, "cannot use at_most with at_least unset")
self._modifier = AT_MOST
return self
mock(self)
Return the mock associated with this expectation.
Returns:
Type | Description |
---|---|
Mock |
Mock associated with this expectation. |
Examples:
>>> plane = flexmock(plane).should_receive("fly").mock()
Source code in flexmock/_api.py
def mock(self) -> Mock:
"""Return the mock associated with this expectation.
Returns:
Mock associated with this expectation.
Examples:
>>> plane = flexmock(plane).should_receive("fly").mock()
"""
self._called_deprecated_property = False
return self._mock
never(self)
Expect expectation's method to never be called.
Alias for times(0)
.
Returns:
Type | Description |
---|---|
Expectation |
Self, i.e. can be chained with other Expectation methods. |
Examples:
>>> flexmock(plane).should_receive("crash").never()
<flexmock._api.Expectation object at ...>
>>> plane.crash()
Traceback (most recent call last):
...
flexmock.exceptions.MethodCallError: crash() expected to be called...
Source code in flexmock/_api.py
def never(self) -> "Expectation":
"""Expect expectation's method to never be called.
Alias for `times(0)`.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("crash").never()
<flexmock._api.Expectation object at ...>
>>> plane.crash()
Traceback (most recent call last):
...
flexmock.exceptions.MethodCallError: crash() expected to be called...
"""
self._called_deprecated_property = False
return self.times(0)
once(self)
Expect expectation's method to be called once.
Alias for times(1)
.
Returns:
Type | Description |
---|---|
Expectation |
Self, i.e. can be chained with other Expectation methods. |
Examples:
>>> flexmock(plane).should_receive("land").once()
<flexmock._api.Expectation object at ...>
>>> plane.land()
Source code in flexmock/_api.py
def once(self) -> "Expectation":
"""Expect expectation's method to be called once.
Alias for `times(1)`.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("land").once()
<flexmock._api.Expectation object at ...>
>>> plane.land()
"""
self._called_deprecated_property = False
return self.times(1)
one_by_one(self)
Modifies the return value to be treated as a list of return values.
Each value in the list is returned on successive invocations of the method.
Returns:
Type | Description |
---|---|
Expectation |
Self, i.e. can be chained with other Expectation methods. |
Examples:
>>> flexmock(plane).should_receive("pilot").and_return("pilot1", "pilot2").one_by_one()
<flexmock._api.Expectation object at ...>
>>> plane.pilot()
'pilot1'
>>> plane.pilot()
'pilot2'
Source code in flexmock/_api.py
def one_by_one(self) -> "Expectation":
"""Modifies the return value to be treated as a list of return values.
Each value in the list is returned on successive invocations of the method.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("pilot").and_return("pilot1", "pilot2").one_by_one()
<flexmock._api.Expectation object at ...>
>>> plane.pilot()
'pilot1'
>>> plane.pilot()
'pilot2'
"""
if not self._callable:
self.__raise(FlexmockError, "can't use one_by_one() with attribute stubs")
if not self._one_by_one:
self._one_by_one = True
return_values = self._return_values
saved_values = return_values[:]
self._return_values = return_values = []
for value in saved_values:
try:
for val in value.value: # type: ignore
return_values.append(ReturnValue(val))
except TypeError:
return_values.append(value)
return self
ordered(self)
Makes the expectation respect the order of should_receive
statements.
An exception will be raised if methods are called out of order, determined
by order of should_receive
calls in the test.
Returns:
Type | Description |
---|---|
Expectation |
Self, i.e. can be chained with other Expectation methods. |
Examples:
>>> flexmock(plane).should_receive("fly").with_args("east").ordered()
<flexmock._api.Expectation object at ...>
>>> flexmock(plane).should_receive("fly").with_args("west").ordered()
<flexmock._api.Expectation object at ...>
>>> plane.fly("west")
Traceback (most recent call last):
...
flexmock.exceptions.CallOrderError: fly("west") called before...
Source code in flexmock/_api.py
def ordered(self) -> "Expectation":
"""Makes the expectation respect the order of `should_receive` statements.
An exception will be raised if methods are called out of order, determined
by order of `should_receive` calls in the test.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("fly").with_args("east").ordered()
<flexmock._api.Expectation object at ...>
>>> flexmock(plane).should_receive("fly").with_args("west").ordered()
<flexmock._api.Expectation object at ...>
>>> plane.fly("west")
Traceback (most recent call last):
...
flexmock.exceptions.CallOrderError: fly("west") called before...
"""
if not self._callable:
self.__raise(FlexmockError, "can't use ordered() with attribute stubs")
self._ordered = True
FlexmockContainer.ordered.append(self)
return self
replace_with(self, function)
Gives a function to run instead of the mocked out one.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
function |
Callable[..., Any] |
A callable that is used to replace the original function. |
required |
Returns:
Type | Description |
---|---|
Expectation |
Self, i.e. can be chained with other Expectation methods. |
Examples:
>>> flexmock(plane).should_receive("set_speed").replace_with(lambda x: x == 5)
<flexmock._api.Expectation object at ...>
>>> plane.set_speed(5)
True
>>> plane.set_speed(4)
False
Source code in flexmock/_api.py
def replace_with(self, function: Callable[..., Any]) -> "Expectation":
"""Gives a function to run instead of the mocked out one.
Args:
function: A callable that is used to replace the original function.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("set_speed").replace_with(lambda x: x == 5)
<flexmock._api.Expectation object at ...>
>>> plane.set_speed(5)
True
>>> plane.set_speed(4)
False
"""
if not self._callable:
self.__raise(FlexmockError, "can't use replace_with() with attribute/property stubs")
replace_with = self._replace_with
original = self.__dict__.get("_original")
if replace_with:
self.__raise(FlexmockError, "replace_with cannot be specified twice")
if function == original:
self._pass_thru = True
self._replace_with = function
return self
times(self, number)
Number of times this expectation's method is expected to be called.
There are also 3 aliases for the times()
method:
once()
->times(1)
twice()
->times(2)
never()
->times(0)
Parameters:
Name | Type | Description | Default |
---|---|---|---|
number |
int |
Expected call count. |
required |
Returns:
Type | Description |
---|---|
Expectation |
Self, i.e. can be chained with other Expectation methods. |
Examples:
>>> flexmock(plane).should_receive("fly").times(1)
<flexmock._api.Expectation object at ...>
>>> plane.fly()
>>> flexmock(plane).should_call("land").times(2)
<flexmock._api.Expectation object at ...>
>>> plane.land()
>>> plane.land()
Source code in flexmock/_api.py
def times(self, number: int) -> "Expectation":
"""Number of times this expectation's method is expected to be called.
There are also 3 aliases for the `times()` method:
- `once()` -> `times(1)`
- `twice()` -> `times(2)`
- `never()` -> `times(0)`
Args:
number: Expected call count.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("fly").times(1)
<flexmock._api.Expectation object at ...>
>>> plane.fly()
>>> flexmock(plane).should_call("land").times(2)
<flexmock._api.Expectation object at ...>
>>> plane.land()
>>> plane.land()
"""
if not self._callable:
self.__raise(FlexmockError, "can't use times() with attribute stubs")
expected_calls = self._expected_calls
modifier = self._modifier
expected_calls[modifier] = number
return self
twice(self)
Expect expectation's method to be called twice.
Alias for times(2)
.
Returns:
Type | Description |
---|---|
Expectation |
Self, i.e. can be chained with other Expectation methods. |
Examples:
>>> flexmock(plane).should_receive("fly").twice()
<flexmock._api.Expectation object at ...>
>>> plane.fly()
>>> plane.fly()
Source code in flexmock/_api.py
def twice(self) -> "Expectation":
"""Expect expectation's method to be called twice.
Alias for `times(2)`.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("fly").twice()
<flexmock._api.Expectation object at ...>
>>> plane.fly()
>>> plane.fly()
"""
self._called_deprecated_property = False
return self.times(2)
when(self, func)
Sets an outside resource to be checked when asserting if a method should be called.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
func |
Callable[..., Any] |
Function that indicates if the mocked or spied method should have been called. |
required |
Returns:
Type | Description |
---|---|
Expectation |
Self, i.e. can be chained with other Expectation methods. |
Examples:
>>> flexmock(plane).should_receive("land").when(lambda: plane.is_flying)
<flexmock._api.Expectation object at ...>
>>> plane.is_flying
True
>>> plane.land()
>>> plane.is_flying = False
>>> plane.land()
Traceback (most recent call last):
...
flexmock.exceptions.StateError: land expected to be called when...
Source code in flexmock/_api.py
def when(self, func: Callable[..., Any]) -> "Expectation":
"""Sets an outside resource to be checked when asserting if a method
should be called.
Args:
func: Function that indicates if the mocked or spied method should
have been called.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
>>> flexmock(plane).should_receive("land").when(lambda: plane.is_flying)
<flexmock._api.Expectation object at ...>
>>> plane.is_flying
True
>>> plane.land()
>>> plane.is_flying = False
>>> plane.land()
Traceback (most recent call last):
...
flexmock.exceptions.StateError: land expected to be called when...
"""
if not self._callable:
self.__raise(FlexmockError, "can't use when() with attribute stubs")
if not hasattr(func, "__call__"):
self.__raise(FlexmockError, "when() parameter must be callable")
self._runnable = func
return self
with_args(self, *args, **kwargs)
Override the arguments used to match this expectation's method.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
*args |
Any |
Positional arguments. |
() |
**kwargs |
Any |
Keyword arguments. |
{} |
Returns:
Type | Description |
---|---|
Expectation |
Self, i.e. can be chained with other Expectation methods. |
Examples:
Match calls with no arguments:
>>> flexmock(plane).should_receive("fly").with_args()
<flexmock._api.Expectation object at ...>
Match a single argument:
>>> flexmock(plane).should_receive("fly").with_args("east")
<flexmock._api.Expectation object at ...>
Match keyword arguments:
>>> flexmock(plane).should_receive("fly").with_args("up", destination="Oslo")
<flexmock._api.Expectation object at ...>
Match argument type:
>>> flexmock(plane).should_receive("fly").with_args(str)
<flexmock._api.Expectation object at ...>
Match a string using a compiled regular expression:
>>> regex = re.compile("^(up|down)$")
>>> flexmock(plane).should_receive("fly").with_args(regex)
<flexmock._api.Expectation object at ...>
Source code in flexmock/_api.py
def with_args(self, *args: Any, **kwargs: Any) -> "Expectation":
"""Override the arguments used to match this expectation's method.
Args:
*args: Positional arguments.
**kwargs: Keyword arguments.
Returns:
Self, i.e. can be chained with other Expectation methods.
Examples:
Match calls with no arguments:
>>> flexmock(plane).should_receive("fly").with_args()
<flexmock._api.Expectation object at ...>
Match a single argument:
>>> flexmock(plane).should_receive("fly").with_args("east")
<flexmock._api.Expectation object at ...>
Match keyword arguments:
>>> flexmock(plane).should_receive("fly").with_args("up", destination="Oslo")
<flexmock._api.Expectation object at ...>
Match argument type:
>>> flexmock(plane).should_receive("fly").with_args(str)
<flexmock._api.Expectation object at ...>
Match a string using a compiled regular expression:
>>> regex = re.compile("^(up|down)$")
>>> flexmock(plane).should_receive("fly").with_args(regex)
<flexmock._api.Expectation object at ...>
"""
if not self._callable:
self.__raise(FlexmockError, "can't use with_args() with attribute stubs")
self._update_argspec()
if self._argspec:
# do this outside try block as TypeError is way too general and catches
# unrelated errors in the verify signature code
self._verify_signature_match(*args, **kwargs)
self._args = self._normalize_named_args(*args, **kwargs)
else:
self._args = {"kargs": args, "kwargs": kwargs}
return self