1

Why is an autospecced mock of Enum not iterable (has no __iter__ method)?

from unittest.mock import create_autospec
from enum import IntEnum


class IE(IntEnum):
    Q = 1
    W = 2


m = create_autospec(spec=IE, instance=False)

print(hasattr(IE, '__iter__'))  # True
print(hasattr(m, '__iter__'))  # False
print(m)  # <MagicMock spec='IE' id='140008077774128'>
ml = create_autospec(spec=[1,2,3], instance=True)
print(hasattr(ml, '__iter__'))  # True

Python version:

Python 3.10.9 (main, Dec  7 2022, 01:12:00) [GCC 9.4.0] on linux
Daniil Fajnberg
  • 12,753
  • 2
  • 10
  • 41
salius
  • 918
  • 1
  • 14
  • 30

1 Answers1

1

The reason is that __iter__ always has to be an instance method. The Enum base class has no __iter__ defined on it. Its metaclass EnumMeta does.

Remember that enumeration members are also attributes of the class. The type of Enum implements the iterable protocol. Specifically, EnumMeta.__iter__ returns the members of the class (in definition order).

That is the reason hasattr(Enum, "__iter__") returns True. That attribute is defined by its (meta) class.

In general, you should be careful in what you assume to be reflected in your autospecced mock objects. The more "exotic" your attributes/methods are, the less likely the spec is to be correct because it relies on introspection. When in doubt, I would suggest explicitly creating mock attributes as needed, instead of relying on autospec. The following is a passage from a chapter on Autospeccing in unittest.mock:

[Autospeccing] isn't without caveats and limitations however, which is why it is not the default behaviour. In order to know what attributes are available on the spec object, autospec has to introspect (access attributes) the spec. As you traverse attributes on the mock a corresponding traversal of the original object is happening under the hood.

Currently, create_autospec relies on dir to inspect what attributes should be created/mocked. This alone brings a bunch of limitations with it.

Daniil Fajnberg
  • 12,753
  • 2
  • 10
  • 41