-
-
Notifications
You must be signed in to change notification settings - Fork 30.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
unittest.mock spec calls class properties #85934
Comments
When async magic method support was added to unittest.mock.Mock to address issue bpo-26467, it introduced a getattr call [1] that causes class properties to be called when the class is used as a mock spec. This caused a problem for a test in my project when running with Python 3.8 where previously the test worked OK with Python 3.6. The test aims to verify that a class instance is not created if the called code path does not access the class property and thus the class will not create a heavy object unless it's needed (lazy create on access via @Property). As of Python 3.8, the @Property is always called and is called by the mock spec process itself, even though the code path being tested does not access the class @Property. Here is a code snippet that illustrates the @Property calling from the mock spec alone: class SomethingElse(object):
def __init__(self):
self._instance = None
@property
def instance(self):
if not self._instance:
self._instance = 'object' ... def test_property_not_called_with_spec_mock(self):
obj = SomethingElse()
self.assertIsNone(obj._instance)
mock = Mock(spec=obj)
self.assertIsNone(obj._instance) $ ./python -m unittest -v unittest.test.testmock.testmock.MockTest.test_property_not_called_with_spec_mock
test_property_not_called_with_spec_mock (unittest.test.testmock.testmock.MockTest) ... FAIL
======================================================================
FAIL: test_property_not_called_with_spec_mock (unittest.test.testmock.testmock.MockTest)
\----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/vagrant/cpython/Lib/unittest/test/testmock/testmock.py", line 2173, in test_property_not_called_with_spec_mock
self.assertIsNone(obj._instance)
AssertionError: 'object' is not None [1] Line 492 in fb27187
|
Without lines numbers, I cannot test which of the two identical asserts failed. Either comments or msg arguments will differentiate. Nor can I run snippets from two different files. Here is a minimal reproducible self-contained code that demonstrates the claim (which I verified on 3.9 and current master). import unittest
from unittest.mock import Mock
class SomethingElse(object):
def __init__(self):
self._instance = None
@property
def instance(self):
if not self._instance:
self._instance = 'object'
class Test(unittest.TestCase):
def test_property_not_called_with_spec_mock(self):
obj = SomethingElse()
self.assertIsNone(obj._instance, msg='before') # before
mock = Mock(spec=obj)
self.assertIsNone(obj._instance, msg='after') # after
unittest.main() |
Related PR: #29901 |
I'm affected by the same problem: The There are three PRs and one issue proposing different solutions:
All solutions would work for the case depicted here. There is a corner case that is treated differently between the current code and the first three proposals: class X(object):
@property
def foo():
async def bar():
pass
return bar If we spec an instance of this class, The fourth solution will handle this corner case as the current code, but does not fix the issue discussed in this post, which was about avoiding expensive lazy initialization of objects triggered by a property getter. I would suggest to go for the fix in #22209, and include the additional test case from #31348. |
Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu> Co-authored-by: Oleg Iarygin <oleg@arhadthedev.net>
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:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: