Commits

Attila Magyar committed b47f5d9

improved error messages: 'called with different arguments'

  • Participants
  • Parent commits 6fe5e67

Comments (0)

Files changed (5)

src/macaque/core/args.py

     def _different_size(self, args1, args2):
         return len(args1) != len(args2)
     
+    def __iter__(self):
+        return iter(self.args_store)

src/macaque/core/matchers.py

 class AnyMatcher(Matcher):
     def matches(self, other): return True
     def __str__(self): return '_'
+    def __repr__(self): return '_'
 
 class TypeMatcher(Matcher):
     def __init__(self, argType, name): 

src/macaque/core/mocks.py

         actual_call_count = sum(self.args.find_matched(*args, **kargs))
         self._verify_call_count(expected_call_count, actual_call_count, (args, kargs))
 
-    def _verify_call_count(self, expected, actual, printed_args):
-        msg_formatter = MessageFormatter(self.method_name, expected, actual, printed_args)
-        if isinstance(expected, tuple):
-            self._verify_call_interval(expected, actual, msg_formatter)
-        elif expected != actual:
-            raise AssertionError(msg_formatter.unexpected_message())
+
+    def _is_interval(self, expected):
+        return isinstance(expected, tuple)
+
+    def _verify_call_count(self, expected_call_count, actual_call_count, method_args):        
+        msg_formatter = MessageFormatter(self.method_name, \
+                                          method_args, \
+                                          expected_call_count, \
+                                          actual_call_count)        
+        if self._is_interval(expected_call_count):
+            self._verify_call_interval(expected_call_count, actual_call_count, msg_formatter)
+        elif expected_call_count != actual_call_count:
+            raise AssertionError(msg_formatter.unexpected_message(self.args))
 
     def _verify_call_interval(self, expected, actual, formatter):
         if len(expected) != 2:
             raise Exception('Expected call count must be a number or a tuple with 2 element')
         lower, upper = expected
         if self._below_lower_bound(actual, lower): # TODO validate lower<upper if not _
-            raise AssertionError(formatter.unexpected_message())
+            raise AssertionError(formatter.unexpected_message(self.args))
         if self._above_upper_bound(actual, upper):
-            raise AssertionError(formatter.unexpected_message())
+            raise AssertionError(formatter.unexpected_message(self.args))
 
     def _below_lower_bound(self, actual, lower_limit):
         return not isinstance(lower_limit, AnyMatcher) and lower_limit > actual
         self.args.store(call_count +1, *args, **kargs)
         
 class MessageFormatter(object):
-    def __init__(self, method_name, expected_call_count, actual_call_count, args):
+    def __init__(self, method_name, method_args, expected_call_count, actual_call_count):
         self.method_name = method_name
         self.expected_call_count = expected_call_count
         self.actual_call_count = actual_call_count
-        self.args, self.kargs = args
+        self.args, self.kargs = method_args
     
-    def unexpected_message(self):
-        signature = self._method_signature()
-        return "%s expected %s but called %d times" %\
-             (signature, str(self.expected_call_count), self.actual_call_count)
+    def unexpected_message(self, args):
+        signature = self._method_signature(self.args, self.kargs)
+        called_with_different = ["%s * %s" % (self._method_signature(a['args'], a['kargs']), str(a['value'])) for a in args]
+        if called_with_different:
+            return '%s expected %s times, but called with different arguments:\n    %s' % \
+                (signature, str(self.expected_call_count), '\n    '.join(called_with_different))
+        else: 
+            return "%s expected %s but called %d times" %\
+                (signature, str(self.expected_call_count), self.actual_call_count)
     
-    def _method_signature(self):
-        args, kargs = list(map(str, self.args)), ["%s=%s" % (str(k), str(v)) for k, v in self.kargs.items()]
+    def _method_signature(self, args, kargs):
+        args, kargs = list(map(str, args)), ["%s=%s" % (str(k), str(v)) for k, v in kargs.items()]
         return '%s(%s)' % (self.method_name, ', '.join(args + kargs))
         
 class Method(object):
         self.assertEqual([5], self.stub.find_matched((1,2)))
         self.assertEqual([6], self.stub.find_matched(set()))
         self.assertEqual([7], self.stub.find_matched({1:2}))
-
+        
+    def testIterateOverOneElement(self):
+        self.stub.store(1, 'simpleArg', key='keyArg')
+        for a in self.stub:
+            self.assertEqual({'value': 1, 'args': ('simpleArg',), 'kargs': {'key': 'keyArg'}}, a)
+            
+    def testIterateOverMultipleElement(self):
+        self.stub.store(1, 'arg1')
+        self.stub.store(2, 'arg2')
+        self.stub.store(3, 'arg3')
+        result = [a for a in self.stub]
+        self.assertEqual(result[0], {'value': 1, 'args': ('arg1',), 'kargs': {}})
+        self.assertEqual(result[1], {'value': 2, 'args': ('arg2',), 'kargs': {}})
+        self.assertEqual(result[2], {'value': 3, 'args': ('arg3',), 'kargs': {}})
+        
 if __name__ == "__main__":
     unittest.main()

test/macaquetest.py

             self.mock.test() * (1,3)
             self.mock.test() * (_,2)
             self.mock.test() * (2,_)
-    
+            
     @expected(AssertionError)        
     def testCallCountInInValidIntervalThrowsAssertionError(self):
         self.mock.test()
             expected_err = 'test(size(3), _, an_int, key1=val, key=contains("str"))'
             self.assertIn(expected_err, str(e))
             
+    def testErrorMessageShowsThatMockWasCalledWithDifferentArguments(self):
+        try:
+            self.mock.test('x')
+            self.mock.test('y'), self.mock.test('y')
+            with expectation:
+                self.mock.test('z') *1
+        except AssertionError as e:
+            print(e)
+            self.assertIn('different arguments', str(e))
+            self.assertIn('test(x) * 1', str(e))
+            self.assertIn('test(y) * 2', str(e))
+
+    def testErrorMessageShowsThatMockWasCalledWithDifferentArguments_(self):
+        try:
+            self.mock.test('x')
+            with expectation:
+                self.mock.test('y') * (1, _)
+        except AssertionError as e:
+            print(e)
+            self.assertIn('test(x) * 1', str(e))
+        
 
 if __name__ == "__main__":
     unittest.main()