diff -r 377bd6e0f61c Lib/unittest/mock.py --- a/Lib/unittest/mock.py Mon Sep 09 22:40:13 2013 -0700 +++ b/Lib/unittest/mock.py Thu Sep 26 02:29:42 2013 -0700 @@ -48,6 +48,18 @@ # The base class for all mocks is NonCallableMock return issubclass(type(obj), NonCallableMock) +def _is_namedtuple_mock(obj): + return ( + _is_instance_mock(obj) and + isinstance(obj._spec_class, type) and + issubclass(obj._spec_class, tuple) and + # namedtuples directly inherit from tuple so the best we can do + # is assume if it maintains the interface of namedtuple and + # subclasses tuple, that it is a namedtuple + hasattr(obj._spec_class, '_asdict') and + hasattr(obj._spec_class, '_fields') and + hasattr(obj._spec_class, '_replace') + ) def _is_exception(obj): return ( @@ -1664,6 +1676,9 @@ '__sizeof__': lambda self: object.__sizeof__(self), } +def _calculate_namedtuple_length(obj): + return len(obj._spec_class._fields) + _return_values = { '__lt__': NotImplemented, '__gt__': NotImplemented, @@ -1714,15 +1729,21 @@ def _set_return_value(mock, method, name): + # mocks are by default length 0. For namedtuples this is an + # issue because that means they will be falsey by default. + if name == '__len__' and _is_namedtuple_mock(mock): + method.return_value = _calculate_namedtuple_length(mock) + return + fixed = _return_values.get(name, DEFAULT) if fixed is not DEFAULT: method.return_value = fixed return - return_calulator = _calculate_return_value.get(name) - if return_calulator is not None: + return_calculator = _calculate_return_value.get(name) + if return_calculator is not None: try: - return_value = return_calulator(mock) + return_value = return_calculator(mock) except AttributeError: # XXXX why do we return AttributeError here? # set it as a side_effect instead? diff -r 377bd6e0f61c Lib/unittest/test/testmock/testhelpers.py --- a/Lib/unittest/test/testmock/testhelpers.py Mon Sep 09 22:40:13 2013 -0700 +++ b/Lib/unittest/test/testmock/testhelpers.py Thu Sep 26 02:29:42 2013 -0700 @@ -5,6 +5,8 @@ Mock, ANY, _CallList, patch, PropertyMock ) +from collections import namedtuple + from datetime import datetime class SomeClass(object): @@ -480,6 +482,27 @@ self.assertRaises(AttributeError, getattr, mock.attr, 'foo') + def test_length_of_namedtuple(self): + Foo = namedtuple('Foo', 'a b c') + + mock = create_autospec(Foo) + self.assertEquals(3, len(mock)) + + + def test_truthiness_of_namedtuple_with_field(self): + Foo = namedtuple('Foo', 'bar') + + mock = create_autospec(Foo) + self.assertEquals(True, bool(mock)) + + + def test_truthiness_of_namedtuple_without_field(self): + Foo = namedtuple('Foo', '') + + mock = create_autospec(Foo) + self.assertEquals(False, bool(mock)) + + def test_method_calls(self): class Sub(SomeClass): attr = SomeClass()