commit 35b44e3c7d5090806922cd0775775bb2985f8a9b Author: Damian Johnson atagar@torproject.org Date: Wed Jun 12 21:48:44 2013 -0700
Dropping homemade mocking functions
Done replacing our mocking methods with the mock module. It's nice to finally be able to drop these... --- test/mocking.py | 315 ------------------------------------------------------- 1 file changed, 315 deletions(-)
diff --git a/test/mocking.py b/test/mocking.py index 3b165d8..35edf81 100644 --- a/test/mocking.py +++ b/test/mocking.py @@ -10,22 +10,7 @@ calling :func:`test.mocking.revert_mocking`.
::
- mock - replaces a function with an alternative implementation - mock_method - replaces a method with an alternative implementation - revert_mocking - reverts any changes made by the mock function - get_real_function - provides the non-mocked version of a function get_all_combinations - provides all combinations of attributes - support_with - makes object be compatible for use via the 'with' keyword - get_object - get an arbitrary mock object of any class - - Mocking Functions - no_op - does nothing - return_value - returns a given value - return_true - returns True - return_false - returns False - return_none - returns None - return_for_args - return based on the input arguments - raise_exception - raises an exception when called
Instance Constructors get_message - stem.response.ControlMessage @@ -56,7 +41,6 @@ calling :func:`test.mocking.revert_mocking`.
import base64 import hashlib -import inspect import itertools
import stem.descriptor.extrainfo_descriptor @@ -68,18 +52,6 @@ import stem.prereq import stem.response import stem.util.str_tools
-# Once we've mocked a function we can't rely on its __module__ or __name__ -# attributes, so instead we associate a unique 'mock_id' attribute that maps -# back to the original attributes. - -MOCK_ID = itertools.count(0) - -# mock_id => (module, function_name, original_function, is_static) - -MOCK_STATE = {} - -BUILTIN_TYPE = type(open) - CRYPTO_BLOB = """ MIGJAoGBAJv5IIWQ+WDWYUdyA/0L8qbIkEVH/cwryZWoIaPAzINfrw1WfNZGtBmg skFtXhOHHqTRN4GPPrZsAIUOQGzQtGb66IQgT4tO/pj+P6QmSCCdTfhvGfgTCsC+ @@ -204,261 +176,6 @@ NETWORK_STATUS_DOCUMENT_FOOTER = ( )
-def no_op(): - def _no_op(*args, **kwargs): - pass - - return _no_op - - -def return_value(value): - def _return_value(*args, **kwargs): - return value - - return _return_value - - -def return_true(): - return return_value(True) - - -def return_false(): - return return_value(False) - - -def return_none(): - return return_value(None) - - -def return_for_args(args_to_return_value, default = None, is_method = False): - """ - Returns a value if the arguments to it match something in a given - 'argument => return value' mapping. Otherwise, a default function - is called with the arguments. - - The mapped argument is a tuple (not a list) of parameters to a function or - method. Positional arguments must be in the order used to call the mocked - function, and keyword arguments must be strings of the form 'k=v'. Keyword - arguments **must** appear in alphabetical order. For example... - - :: - - mocking.mock("get_answer", mocking.return_for_args({ - ("breakfast_menu",): "spam", - ("lunch_menu",): "eggs and spam", - (42,): ["life", "universe", "everything"], - })) - - mocking.mock("align_text", mocking.return_for_args({ - ("Stem", "alignment=left", "size=10"): "Stem ", - ("Stem", "alignment=center", "size=10"): " Stem ", - ("Stem", "alignment=right", "size=10"): " Stem", - })) - - mocking.mock_method(Controller, "new_circuit", mocking.return_for_args({ - (): "1", - ("path=['718BCEA286B531757ACAFF93AE04910EA73DE617', " + \ - "'30BAB8EE7606CBD12F3CC269AE976E0153E7A58D', " + \ - "'2765D8A8C4BBA3F89585A9FFE0E8575615880BEB']",): "2" - ("path=['1A', '2B', '3C']", "purpose=controller"): "3" - }, is_method = True)) - - :param dict args_to_return_value: mapping of arguments to the value we should provide - :param functor default: returns the value of this function if the args don't - match something that we have, we raise a ValueError by default - :param bool is_method: handles this like a method, removing the 'self' - reference - """ - - def _return_value(*args, **kwargs): - # strip off the 'self' if we're mocking a method - if args and is_method: - args = args[1:] if len(args) > 2 else [args[1]] - - if kwargs: - args.extend(["%s=%s" % (k, kwargs[k]) for k in sorted(kwargs.keys())]) - - args = tuple(args) - - if args in args_to_return_value: - return args_to_return_value[args] - elif default is None: - arg_label = ", ".join([str(v) for v in args]) - arg_keys = ", ".join([str(v) for v in args_to_return_value.keys()]) - raise ValueError("Unrecognized argument sent for return_for_args(). Got '%s' but we only recognize '%s'." % (arg_label, arg_keys)) - else: - return default(args) - - return _return_value - - -def raise_exception(exception): - def _raise(*args, **kwargs): - raise exception - - return _raise - - -def support_with(obj): - """ - Provides no-op support for the 'with' keyword, adding __enter__ and __exit__ - methods to the object. The __enter__ provides the object itself and __exit__ - does nothing. - - :param object obj: object to support the 'with' keyword - - :returns: input object - """ - - if not hasattr(obj, "__enter__"): - setattr(obj, "__enter__", return_value(obj)) - - if not hasattr(obj, "__exit__"): - setattr(obj, "__exit__", no_op()) - - return obj - - -def mock(target, mock_call, target_module = None, is_static = False): - """ - Mocks the given function, saving the initial implementation so it can be - reverted later. - - The target_module only needs to be set if the results of - 'inspect.getmodule(target)' doesn't match the module that we want to mock - (for instance, the 'os' module provides the platform module that it wraps - like 'postix', which won't work). - - :param function target: function to be mocked - :param functor mock_call: mocking to replace the function with - :param module target_module: module that this is mocking, this defaults to the inspected value - :param bool is_static: handles this like a static method of the target_module if True - """ - - if hasattr(target, "mock_id"): - # we're overriding an already mocked function - mocking_id = getattr(target, "mock_id") - target_module, target_function, _, _ = MOCK_STATE[mocking_id] - else: - # this is a new mocking, save the original state - mocking_id = MOCK_ID.next() - target_module = target_module or inspect.getmodule(target) - target_function = target.__name__ - MOCK_STATE[mocking_id] = (target_module, target_function, target, is_static) - - mock_wrapper = lambda *args, **kwargs: mock_call(*args, **kwargs) - setattr(mock_wrapper, "mock_id", mocking_id) - - # mocks the function with this wrapper - - if is_static: - setattr(target_module, target_function, staticmethod(mock_wrapper)) - else: - setattr(target_module, target_function, mock_wrapper) - - -def mock_method(target_class, method_name, mock_call): - """ - Mocks the given method in target_class in a similar fashion as mock() - does for functions. For instance... - - :: - - >>> mock_method(stem.control.Controller, "is_feature_enabled", mocking.return_true()) - >>> controller.is_feature_enabled("VERBOSE_EVENTS") - True - - :: - - "VERBOSE_EVENTS" does not exist and can never be True, but the mocked - "is_feature_enabled" will always return True, regardless. - - :param class target_class: class with the method we want to mock - :param str method_name: name of the method to be mocked - :param functor mock_call: mocking to replace the method - """ - - # Ideally callers could call us with just the method, for instance like... - # mock_method(MyClass.foo, mocking.return_true()) - # - # However, while classes reference the methods they have the methods - # themselves don't reference the class. This is unfortunate because it means - # that we need to know both the class and method we're replacing. - - target_method = getattr(target_class, method_name) - - if hasattr(target_method, "mock_id"): - # we're overriding an already mocked method - mocking_id = target_method.mock_id - _, target_method, _, _ = MOCK_STATE[mocking_id] - else: - # this is a new mocking, save the original state - mocking_id = MOCK_ID.next() - MOCK_STATE[mocking_id] = (target_class, method_name, target_method, False) - - mock_wrapper = lambda *args, **kwargs: mock_call(*args, **kwargs) - setattr(mock_wrapper, "mock_id", mocking_id) - - # mocks the function with this wrapper - setattr(target_class, method_name, mock_wrapper) - - -def revert_mocking(): - """ - Reverts any mocking done by this function. - """ - - # Reverting mocks in reverse order. If we properly reuse mock_ids then this - # shouldn't matter, but might as well be safe. - - mock_ids = MOCK_STATE.keys() - mock_ids.sort() - mock_ids.reverse() - - for mock_id in mock_ids: - module, function, impl, is_static = MOCK_STATE[mock_id] - - # Python 3.x renamed __builtin__ to builtins. Ideally we'd account for - # this with a simple 'import __builtin__ as builtins' but that somehow - # makes the following check fail. Haven't a clue why. - - if stem.prereq.is_python_3(): - import builtins - builtin_module = builtins - else: - import __builtin__ - builtin_module = __builtin__ - - if is_static: - impl = staticmethod(impl) - - if module == builtin_module: - setattr(builtin_module, function, impl) - else: - setattr(module, function, impl) - - del MOCK_STATE[mock_id] - - MOCK_STATE.clear() - - -def get_real_function(function): - """ - Provides the original, non-mocked implementation for a function or method. - This simply returns the current implementation if it isn't being mocked. - - :param function function: function to look up the original implementation of - - :returns: original implementation of the function - """ - - if hasattr(function, "mock_id"): - mocking_id = getattr(function, "mock_id") - return MOCK_STATE[mocking_id][2] - else: - return function - - def get_all_combinations(attr, include_empty = False): """ Provides an iterator for all combinations of a set of attributes. For @@ -496,38 +213,6 @@ def get_all_combinations(attr, include_empty = False): yield item
-def get_object(object_class, methods = None): - """ - Provides a mock instance of an arbitrary class. Its methods are mocked with - the given replacements, and calling any others will result in an exception. - - :param class object_class: class that we're making an instance of - :param dict methods: mapping of method names to their mocked implementation - - :returns: stem.control.Controller instance - """ - - if methods is None: - methods = {} - - mock_methods = {} - - for method_name in dir(object_class): - if method_name in methods: - mock_methods[method_name] = methods[method_name] - elif method_name.startswith('__') and method_name.endswith('__'): - pass # messing with most private methods makes for a broken mock object - else: - mock_methods[method_name] = raise_exception(ValueError("Unexpected call of '%s' on a mock object" % method_name)) - - # makes it so our constructor won't need any arguments - mock_methods['__init__'] = no_op() - - mock_class = type('MockClass', (object_class,), mock_methods) - - return mock_class() - - def get_message(content, reformat = True): """ Provides a ControlMessage with content modified to be parsable. This makes
tor-commits@lists.torproject.org