Carlos Ble avatar Carlos Ble committed 8141050

python fucking rocks. we can now assign method stubs to variables and handle them independently. wow

Comments (0)

Files changed (3)

pyDoubles/core.py

 class ApiMismatch(Exception):
     pass
 
+class _Interceptor_():
+    
+    def __init__(self, double, attr):
+        self.double = double
+        self.attr_name = attr
+        
+    def __call__(self, *args, **kwargs):
+        self.double.invoked_method_name = self.attr_name
+        return self.double._on_target_method_call(self, *args, 
+                                                  **kwargs)
+    
+    
 class _Introspector_():
     """
     Internal framework class. Specialized in introspection (reflection)
     """    
     
-    def method_name(self, method):
-        return method.im_func.func_name
+    def method_name(self, interceptor):
+        return interceptor.attr_name
     
-    def double_instance_from_method(self, method):
+    def double_instance_from_method(self, interceptor):
         try:
-            instance = method.im_self
-            if hasattr(instance, "_double"):
-                return instance._double
-            return instance
-        except AttributeError:
+            return interceptor.double
+        except AttributeError,e:
             raise WrongApiUsage("Make sure you call this framework method passing in a spy or mock object")
     
     def original_method_signature(self, original_method):
            self._kwargs(kwargs) not in self.calls_args_per_method[method_name]:
                 raise ArgsDontMatch(self._format_err_msg(
                          method_name, str(args), str(kwargs)))
+                
+    def show_registered_calls(self):
+        return str(self.calls_args_per_method)
 
 
         

pyDoubles/framework.py

 from core import _CallsRegistry_, _StubsRepository_
-from core import _EmptyObject_, _Introspector_
+from core import _EmptyObject_, _Introspector_, _Interceptor_
 from core import UnexpectedBehavior, WrongApiUsage, ApiMismatch
 from core import ArgsDontMatch, ANY_ARG
                  
         self.original_instance = original_instance
         self.original_instance._double = self
         self.asserting_on_method = None
+        self.invoked_method_name = None
 
     def __getattr__(self, attr):
-        if self.introspector.attr_was_invoked_as_method(attr):
-           self.invoked_method_name = attr
-           return self._interceptor
-        return getattr(self.original_instance, attr)
+        return _Interceptor_(self, attr)
         
     def _before_target_method(self, args, kwargs):
         self._replace_possible_alias_method_name()
         self.registry.register_call(self.invoked_method_name, 
                                             *args, **kwargs)
         
-    def _interceptor(self, *args, **kwargs):
+    def __remove_interceptor_from_call_args(self, args):
+        return args[1:]
+    
+    def _on_target_method_call(self, *args, **kwargs):
+        args = self.__remove_interceptor_from_call_args(args)
         self._before_target_method(args, kwargs)
         if self.stubs.is_matching_stubbed_method(self.invoked_method_name, 
                                     args, kwargs):
     
     def assert_that_is_satisfied(self):
         if len(self.stubbed_method_names) > len(self.satisfied_expectations):
-           raise UnexpectedBehavior("Defined expectations were not satisfied")
+           raise UnexpectedBehavior("Defined expectations were not satisfied. Registered calls are:" + self.registry.show_registered_calls())
 
        
 # PUBLIC STATIC METHODS:        

pyDoublesTests/unit.py

     
     alias_method = one_arg_method
 
+
 class ProxySpyTests(unittest.TestCase):
     
     def setUp(self):
     def test_stub_works_with_alias_method(self):
         when(self.spy.one_arg_method).with_args(1).then_return(1000)
         
-        #self.assertEquals(1000, self.spy.alias_method(1))                                
         self.spy.alias_method(1)
         assert_that_was_called(self.spy.one_arg_method
                                ).with_args(1)                
 
-
 class CombinatorTests(unittest.TestCase):
     def setUp(self):
         self.combinator = _Combinator_()
         
         assert_that_was_called(self.spy.one_arg_method).with_args(
                                                         non_ascii)    
+
+    def test_stub_methods_can_be_handled_separately(self):
+        when(self.spy.one_arg_method).with_args(1).then_return(1000)
+        when(self.spy.two_args_method).with_args(5,5).then_return(2000)
+        handle1 = self.spy.one_arg_method
+        handle2 = self.spy.two_args_method
+        self.assertEquals(1000, handle1(1))
+        self.assertEquals(2000, handle2(5,5))
+        
+        assert_that_was_called(handle1).with_args(1)
+        assert_that_was_called(handle2).with_args(5,5)
         
 class MockTests(unittest.TestCase):
     
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.