Improve UX for mocking dataclasses that are problematic to instantiate
@sobolevn 已經在處理了。
開始於 2024年9月18日。
- 主要語言
- Python
- 星號
- 77.2k
- 分支
- 35.9k
- PR 合併指標
- PR 指標待擷取
描述
Feature or enhancement
Proposal:
Passing instance=True to create_autospec misses fields without default values, even when the given spec is a dataclass object:
>>> from dataclasses import dataclass
>>> @dataclass
... class Example:
... a: int
... b: int = 0
... def sum_fields(self):
... return self.a + self.b
...
>>> from unittest.mock import create_autospec
>>> mock_instance = create_autospec(Example(1))
>>> mock_instance_from_class = create_autospec(Example, instance=True)
>>> set(dir(mock_instance)) - set(dir(mock_instance_from_class))
{'a'}
This is despite dataclass definitions making their field information readily available for introspection on the class object (without requiring instantiation):
>>> from dataclasses import fields
>>> [(field.name, field.type) for field in fields(Example)]
[('a', <class 'int'>), ('b', <class 'int'>)]
A similar problem occurs if the dataclass uses __post_init__ to set attributes that are not otherwise set:
>>> from dataclasses import dataclass, field
>>> @dataclass
... class PostInitExample:
... a: int = field(init=False)
... b: int = field(init=False)
... def __post_init__(self):
... self.a = self.b = 1
...
>>> mock_post_init_class = create_autospec(Example)
>>> mock_post_init_class = create_autospec(PostInitExample)
>>> mock_post_init_instance = create_autospec(PostInitExample())
>>> set(dir(mock_post_init_instance)) - set(dir(mock_post_init_class()))
{'b', 'a'}
While for most dataclasses it is straightforward to instantiate a specific instance and derive the mock autospec from that, this may not be desirable (or even feasible) if the dataclass requires references to real external resources to create a real instance.
There are various potential workarounds available for this functional gap, but they're all relatively clumsy, and come with their own problems (like not being able to use spec_set=True if the missing fields are added manually, or not handling defined methods properly if setting an explicit list of fields instead of using autospec, or still not adding the fields only defined in __post_init__ if instantiating a class mock).
By contrast, if create_autospec were to be made explicitly aware of data classes, it could do a pass over dataclasses.fields(spec) and use the type information to fill in any missing fields that don't have class level default values set.
Has this already been discussed elsewhere?
This is a minor feature, which does not need previous discussion elsewhere
Links to previous discussion of this feature:
Previously filed here, but closed on the basis of create_autospec covering the use case: https://github.com/python/cpython/issues/80761
This is only true if the dataclass can be readily instantiated, hence this feature request.
There is also some previous discussion (and assorted attempted workarounds with various flaws) on this Stack Overflow question: https://stackoverflow.com/questions/51640505/how-to-use-spec-when-mocking-data-classes-in-python
Linked PRs
- gh-124429
- gh-124724
貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
評估
這個 Issue 還沒有評估資料。