cpython icon indicating copy to clipboard operation
cpython copied to clipboard

Consider improving the clarity of unittest.mock.create_autospec

Open 743a96da-0d8c-49d0-8d26-89fb8eaa6e3b opened this issue 8 years ago • 4 comments

BPO 30548
Nosy @csabella, @mariocj89, @tirkarthi

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = None
created_at = <Date 2017-06-02.09:06:21.041>
labels = ['type-feature', 'docs']
title = 'typo in documentation for create_autospec'
updated_at = <Date 2019-10-30.08:35:30.452>
user = 'https://bugs.python.org/ErikBystrm'

bugs.python.org fields:

activity = <Date 2019-10-30.08:35:30.452>
actor = 'Erik Bystr\xc3\xb6m'
assignee = 'docs@python'
closed = False
closed_date = None
closer = None
components = ['Documentation']
creation = <Date 2017-06-02.09:06:21.041>
creator = 'Erik Bystr\xc3\xb6m'
dependencies = []
files = []
hgrepos = []
issue_num = 30548
keywords = []
message_count = 4.0
messages = ['294993', '305609', '334361', '355697']
nosy_count = 5.0
nosy_names = ['docs@python', 'cheryl.sabella', 'mariocj89', 'Erik Bystr\xc3\xb6m', 'xtreak']
pr_nums = []
priority = 'normal'
resolution = None
stage = None
status = 'open'
superseder = None
type = 'enhancement'
url = 'https://bugs.python.org/issue30548'
versions = ['Python 3.6']

"a class" should most probably be replaced by "an instance" in the documentation for create_autospec.

"You can use a class as the spec for an instance object by passing instance=True. The returned mock will only be callable if instances of the mock are callable."

https://docs.python.org/3/library/unittest.mock.html#unittest.mock.create_autospec

I've always understood instance as a way to say "I am passing this class but I want to force the autospec on the instance"

For example, given

class X:
    def __init__(self):
        raise

You can do unittest.mock.create_autospec(X, instance=True) to set a spec of the instance rather than the class.

Also quite often you do autospec on a class but you want the interface of the instance. This parameter allows you to do so.

Basically, unittest.mock.create_autospec(X, instance=True) will produce a non callable mock.

I think the docs are correct, maybe misleading

Mario is right that this isn't a typo. Here's a code example to illustrate what he said:

>>> class MyClass:
...     a = 3
...     def foo(self): pass
...
>>> mock_class = create_autospec(MyClass)
>>> mock_class
<MagicMock spec='MyClass' id='16678696'>
>>> mock_class()
<NonCallableMagicMock name='mock()' spec='MyClass' id='16752016'>
>>> mock_class.foo
<MagicMock name='mock.foo' spec='function' id='16751032'>

>>> mock_instance = create_autospec(MyClass, instance=True)
>>> mock_instance
<NonCallableMagicMock spec='MyClass' id='16757832'>
>>> mock_instance()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NonCallableMagicMock' object is not callable
>>> mock_instance.foo
<MagicMock name='mock.foo' spec='function' id='16750024'>

As per the docs, the instance object uses the class as the spec and it isn't callable, whereas the mock class is. Would adding this example to the docs help or would a different code example help make this less misleading?

csabella avatar Jan 25 '19 13:01 csabella

Yes, you're right. I do think the docs are a bit misleading.

Maybe something like this would make it more clear?

"If a class is used as a spec then the returned object will be a mock of that class. When the constructor of the returned mock class is invoked an instance object is returned that has the same spec as a normal instance object would have. By passing instance=True you can directly create a mock instance of the class."

If not, feel free to close this issue.