You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I expected this to happen:
The test test_parent_and_child_no_override should pass
But, instead this happened:
When both the child and parent classes are mocked I get a NonExistentAttribute exception when I attempt to mock a non-overridden method in the child class:
% testslide test.py
test.TestThing
test_child
test_parent
test_parent_and_child_no_override: NonExistentAttribute: 'a_method' can not be set.
test_parent_and_child_no_override_careful_ordering
test_parent_and_child_override
Failures:
1) test.TestThing: test_parent_and_child_no_override
1) NonExistentAttribute: 'a_method' can not be set.
<StrictMock 0x10EF3CBD0 template=test.AChild test.py:32> template class does not have this attribute so the mock can not have it as well.
See also: 'runtime_attrs' at StrictMock.__init__.
File "/Users/nsheridan/homebrew/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/contextlib.py", line 119, in __exit__
next(self.gen)
File "/Users/nsheridan/homebrew/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py", line 59, in testPartExecutor
yield
File "/Users/nsheridan/homebrew/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py", line 628, in run
testMethod()
File "test.py", line 38, in test_parent_and_child_no_override
self.mock_callable(achild, "a_method").to_return_value(None)
Finished 5 example(s) in 0.2s: .
Successful: 4
Failed: 1
As you can see from the example, it is possible to
mock a method in a child class before the parent is mocked
mock a method in a child class after the parent is mocked if the method is overridden in the child
However if the parent and child classes are both mocked it is not possible to mock non-overridden methods in the child class
The text was updated successfully, but these errors were encountered:
This bug is a result of the extremely hacky way that mock_constructor is implemented due to an upstream Python bug...
The gist is:
When self.mock_constructor(sys.modules[__name__], "AClass").to_return_value(aclass) is called:
It must empty all attributes of AClass,
Create a subclass of it,
Put all attributes back at the subclass (but mocked),
Finally put this subclass in place of AClass at the module.
So, when achild = testslide.StrictMock(AChild) runs, it gets a reference to the original AChild class. But when self.mock_constructor(sys.modules[__name__], "AChild").to_return_value(achild) runs, it creates the new mocked class, which should be accessed via getattr(sys.modules[__name__], "AChild") exclusively, however, achild still has the reference for the old empty class.
mock_constructor has safeguards in place against similar cases, and it'll refuse to work if there are pre-existing instances of the mocked class. But the check, is currently not covering this case and needs improvement.
I drafted a fix:
diff--gita/testslide/mock_constructor.pyb/testslide/mock_constructor.pyindexa85fa6a..0b12ef5100644---a/testslide/mock_constructor.py+++b/testslide/mock_constructor.py
@@ -315,13+315,21 @@ defmock_constructor(target, class_name, allow_private=False, type_validation=Trinstances= [
objforobjingc.get_referrers(original_class)
-iftype(obj) isoriginal_class+# FIXME exclude all allowed cases, eg: parent class, references at this module.+ifid(obj) !=id(target)
]
ifinstances:
+references_str="> "+"\n\n > ".join(f"{id(obj)}: {type(obj)}\n{repr(obj)}"forobjininstances)
raiseRuntimeError(
-"mock_constructor() can not be used after instances of {} were created: {}".format(
-class_name, instances- )
+"mock_constructor() can not be used with multiple references to the class:\n"+"Due to an upstream Python bug, mock_constructor() implementation contains "+"a lot of workarounds "+"(https://testslide.readthedocs.io/en/master/patching/mock_constructor/index.html#implementation-details). "+"As a result, it is impossible to support its usage in scenarios where:\n"+"- Instances of the class have already been created\n."+"- Multiple references to the class exist.\n"+f"The following live objects have references to {class_name}:\n\n"++references_str
)
ifnotinspect.isclass(original_class):
But as the FIXME comment tells, it still needs some love, this is gonna be tricky, not only to add all exceptions, but to also, give a meaningful error. Eg: the object with the reference can be a dictionary of another class (eg: OtherClass.whatever = AChild), and just printing this dictionary at the error, won't make it obvious that it belongs to OtherClass.
I'm using
TestSlide
version: 2.5.7Given: https://gist.github.com/nsheridan/a08dc79a84cc2974710b7a02b78ff77b
When I run:
I expected this to happen:
The test
test_parent_and_child_no_override
should passBut, instead this happened:
When both the child and parent classes are mocked I get a
NonExistentAttribute
exception when I attempt to mock a non-overridden method in the child class:As you can see from the example, it is possible to
However if the parent and child classes are both mocked it is not possible to mock non-overridden methods in the child class
The text was updated successfully, but these errors were encountered: