wrote simple mocking helper methods and classes
authorFredrik Wendt <fredrik@wendt.se>
Mon, 23 Aug 2010 21:44:39 +0000 (22:44 +0100)
committerFredrik Wendt <fredrik@wendt.se>
Mon, 23 Aug 2010 21:44:39 +0000 (22:44 +0100)
package/test/unit/pie.py [new file with mode: 0644]
package/test/unit/test_pie.py [new file with mode: 0644]

diff --git a/package/test/unit/pie.py b/package/test/unit/pie.py
new file mode 100644 (file)
index 0000000..e605b28
--- /dev/null
@@ -0,0 +1,181 @@
+import sys
+
+def pprint(*args):
+    if False:
+        for a in args:
+            try:
+                sys.stdout.write(a.__str__())
+            except:
+                try:
+                    sys.stdout.write(a.__doc__)
+                except:
+                    sys.stdout.write('BUG - FIXME')
+            sys.stdout.write(' ')
+        sys.stdout.write('\n')
+        sys.stdout.flush()
+
+class Mock():
+    def __init__(self):
+        pprint("Mock.__init__")
+        self._methods = {}
+        self._replay = False
+        
+    def __getattr__(self, value):
+        pprint("Mock.__getattr__", value)
+        if self._methods.has_key(value):
+            pprint("returning mocked method")
+            return self._methods[value]
+        if self._replay:
+            class inner():
+                def __init__(self, method_name):
+                    self._method_name = method_name
+                    self._recorded_calls = []
+                    self._args = None
+                    self._kwargs = None
+                def __call__(self, *args, **kwargs):
+                    pprint("REPLAYING", self.getName(), args, kwargs)
+                    self._recorded_calls.append((args, kwargs))
+                    self._args = args
+                    self._kwargs = kwargs
+                def getName(self):
+                    return self._method_name
+                def getNumberOfInvocations(self):
+                    return len(self._recorded_calls)
+                def verify(self, args, kwargs):
+                    if args != self._args or kwargs != self._kwargs:
+                        raise Exception("No call for %s with arguments %s - actual: %s" % (value, args, self._args))
+                    return True
+                    
+            pprint("returning 'empty' method", value)
+            empty = inner(value)
+            self.register(value, empty)
+            return empty
+        raise AttributeError("Mock doesn't contain attribute", value)
+        pprint("returning super method")
+        return Object.__getattr__.call(self, value)
+        
+    def replay(self):
+        pprint("Mock.__replay__")
+        self._replay = True
+        
+    def register(self, methodName, mock):
+        pprint("Mock.__register__", methodName)
+        self._methods[methodName] = mock
+    
+    def getMethodMock(self, methodName):
+        pprint("Mock.getMethodMock", methodName)
+        if self._methods.has_key(methodName):
+            return self._methods[methodName]
+        return None
+
+class MethodMock():
+    def __init__(self, mock, args, kwargs):
+        pprint("MethodMock.__init__", args, kwargs)
+        self.target = mock
+        self._method_name = args[0]
+        mock.register(self._method_name, self)
+        self.args = args[1:]
+        self.kwargs = kwargs
+        self._recorded_calls = []
+        self._canned_response = None
+        self._canned_args = None
+        self._canned_args_set = False
+    
+    def willReturn(self, args):
+        pprint(self.getName(), "willReturn", args)
+        self._canned_response = args
+        
+    def verify(self, args, kwargs):
+        pprint("MethodMock.verify", args, kwargs)
+        recorded_args, recorded_kwargs = self._recorded_calls.pop()
+        pprint("          .verify", recorded_args, recorded_kwargs)
+        return args == recorded_args and kwargs == recorded_kwargs
+    
+    def getName(self):
+        return self._method_name
+    
+    def getNumberOfInvocations(self):
+        return len(self._recorded_calls)
+    
+    def __call__(self, *args, **kwargs):
+        if self._canned_args_set: # replay
+            pprint("MethodMock.__call__ REPLAYING", self.getName(), args, kwargs, "returning", self._canned_response)
+            self._recorded_calls.append((args, kwargs))
+            return self._canned_response
+        else:
+            pprint("MethodMock.__call__ RECORDING", self.getName(), args, kwargs)
+            self._canned_args_set = True
+            self._canned_args = args
+            return self
+        
+class given():
+    def __init__(self, mock):
+        pprint("given.__init__")
+        self.target = mock
+    
+    def __getattr__(self, *args, **kwargs):
+        pprint("given.__getattr__", args, kwargs)
+        return MethodMock(self.target, args, kwargs) 
+        
+
+class MethodCallVerifier():
+    def __init__(self, methodMock, times):
+        self.method = methodMock
+        self.times = times
+    def __call__(self, *args, **kwargs):
+        pprint("MethodCallVerifier.__call__", self.method, args, kwargs)
+        # FIXME: verify times times
+        calls = self.method.getNumberOfInvocations()
+        min_calls = self.times.getMinimumNumberOfTimes()
+        max_calls = self.times.getMaximumNumberOfTimes()
+        if calls < min_calls:
+            raise Exception("Expected at least %s calls to %s, got %s" % (min_calls, self.method.getName(), calls))
+        if calls > max_calls:
+            raise Exception("Expected at most %s calls to %s, got %s" % (max_calls, self.method.getName(), calls))
+        if calls == 0:
+            return
+            
+        match = self.method.verify(args, kwargs)
+        if match:
+            pprint("Yes, call for", self.method.getName(), "matched arguments", args, kwargs)
+        else:
+            pprint("Nope, call for", self.method.getName(), "didn't match arguments", args, kwargs)
+            raise Exception("mock verification failed")
+
+def empty(*args, **kwargs):
+    pass
+
+class once():
+    def __call__(self):
+        pass
+    def getMinimumNumberOfTimes(self):
+        return 1
+    def getMaximumNumberOfTimes(self):
+        return 1
+
+class never():
+    def __call__(self):
+        pass
+    def getMinimumNumberOfTimes(self):
+        return 0
+    def getMaximumNumberOfTimes(self):
+        return 0
+
+class verify():
+    def __init__(self, mock, times=None):
+        pprint("verify.__init__")
+        self.target = mock
+        self.times = times
+        if self.times is None:
+            self.times = once()
+        
+    def __getattr__(self, methodName):
+        pprint("verify.__getattr__", methodName)
+        method_mock = self.target.getMethodMock(methodName)
+        if method_mock is None:
+            if self.times.getMinimumNumberOfTimes() > 0:
+                raise Exception("method " + methodName + " was never called")
+            return empty
+        return MethodCallVerifier(method_mock, self.times)
+        
+
diff --git a/package/test/unit/test_pie.py b/package/test/unit/test_pie.py
new file mode 100644 (file)
index 0000000..dcb60a8
--- /dev/null
@@ -0,0 +1,48 @@
+import unittest
+from pie import *
+
+
+class TestPie(unittest.TestCase):
+    
+    def test_never(self):
+        mock = Mock()
+        mock.replay()
+        verify(mock, never()).fakeMethod()
+        
+    def test_neverFail(self):
+        mock = Mock()
+        mock.replay()
+        mock.fakeMethod()
+        try:
+            verify(mock, never()).fakeMethod()
+            raise Exception("The fake method was called - the verify statement should fail")
+        except:
+            pass
+        
+    def test_onceSuccess(self):
+        mock = Mock()
+        given(mock).method().willReturn(True)
+        mock.replay()
+        result = mock.method()
+        assert result is True
+        verify(mock, once()).method()
+    
+    def test_onceFail(self):
+        mock = Mock()
+        given(mock).method().willReturn(True)
+        mock.replay()
+        try:
+            verify(mock, once()).method()
+            raise Exception("The method was never called - the verify step should fail")
+        except:
+            pass
+        
+    def test_implicitOnce_undefinedReturn_WithArguments(self):
+        mock = Mock()
+        mock.replay()
+        url = 'http://sample.argument'
+        mock.openUrl(url)
+        verify(mock).openUrl(url)
+    
+if __name__ == '__main__':
+    unittest.main()