python3模拟不适用于所有路径 [英] python3 mock doesn't work for all paths
问题描述
生产文件(production_file.py)是:
The Production file (production_file.py) is:
class MyError(Exception):
pass
class MyClass:
def __init__(self):
self.value = None
def set_value(self, value):
self.value = value
def foo(self):
raise RuntimeError("error!")
class Caller:
def bar(self, smth):
obj = MyClass()
obj.set_value(smth)
try:
obj.foo()
except MyError:
pass
obj.set_value("str2")
obj.foo()
测试文件(test.py):
Test file (test.py):
import unittest
from unittest.mock import patch
from unittest.mock import call
from production_file import MyClass, Caller
class MyTest(unittest.TestCase):
def test_caller(self):
with patch('production_file.MyClass', autospec=MyClass) as MyClassMock:
my_class_mock_obj = MyClassMock.return_value
my_class_mock_obj.foo.side_effect = [MyError("msg"), "text"]
caller = Caller()
caller.bar("str1")
calls = [call("str1"), call("str2")]
my_class_mock_obj.set_value.assert_has_calls(calls)
if __name__ == '__main__':
unittest.main()
以上方法有效.但是,如果我将生产类(MyError,MyClass,Caller)移到测试文件中,并将补丁更新为:
This above works. But if I move the production classes (MyError, MyClass, Caller) into the test file, and update patch to:
with patch('test.MyClass', autospec=MyClass) as MyClassMock:
然后不再模拟实例方法"foo".
then the instance method "foo" is no longer mocked.
有人知道为什么会这样吗?
Does anybody have any idea why that is?
我还遇到了一些更复杂的代码的类似问题,其中生产代码在my_package/src/production_file.py中,而测试在my_package/tests/test_file.py中. Python的路径没有错误,路径是正确的,但模拟仍然无效.
I have also experienced a similar problem with some more complex code, where the production code is in my_package/src/production_file.py while the test is in my_package/tests/test_file.py. Python yields no error for the path, the path is correct, but still the mock doesn't work.
推荐答案
如果您将test.py
作为__main__
运行,则不是test.MyClass
,而是__main__.MyClass
,或者在两种情况下都是__name__+".MyClass"
.
If you are running test.py
as __main__
then it is not test.MyClass
it would be __main__.MyClass
, or in both cases __name__+".MyClass"
.
通过添加一条打印语句,我可以确定所使用的类和所修补的类是否不同:
I was able to determine that the class used and the class patched were different by adding a print statement:
class Caller:
def bar(self, smth):
print(MyClass) #lets see what we are actually making an instance of...
obj = MyClass()
...
将补丁程序应用于正在使用的类时,您会看到类似以下内容的内容:
When the patch is applied to the class that this is using you would see something like this:
<MagicMock name='MyClass' spec='MyClass' id='4387629656'>
但是当将班级移入test.py
时,您会看到类似以下内容的内容:
But when the class in moved into test.py
you will see something like:
<class '__main__.MyClass'>
哪个表示:
- 没有对
MyClass
(至少是用于测试的补丁)应用补丁. - 需要修补的类的名称为
__main__.MyClass
- There was no patching applied to
MyClass
(at least the one that is used for the test.) - The name of the class that needs to be patched is
__main__.MyClass
但是,由于这样的设置,您的更复杂的情况"很可能无法正常工作:
However It is quite likely that your "more... complicated situation" is not working because of a setup like this:
from production_file import MyClass
class MyError(Exception):
pass
class Caller:
def bar(self, smth):
print(MyClass)
obj = MyClass()
...
class MyTest(unittest.TestCase):
def test_caller(self):
with patch('production_file.MyClass', autospec=MyClass) as MyClassMock:
...
在这种情况下,正在修补production_file.MyClass
并从production_file
导入MyClass
,因此正在修补正确的类,但输出仍然是:
In this case production_file.MyClass
is being patched and MyClass
is being imported from production_file
so the correct class is being patched but still the output is:
<class 'production_file.MyClass'>
这是因为该类直接导入到本地名称空间,因此,当将修补程序应用于production_file
时,本地名称空间仍然不受影响,我们可以检查该修补程序是否实际应用于:
This is because the Class was directly imported to the local namespace, so when the patch is applied to the production_file
the local namespace is still unaffected, we can check that the patch was actually applied with:
...
def bar(self, smth):
print(MyClass)
from production_file import MyClass as pf_MyClass
print(pf_MyClass)
...
#output:
<class 'production_file.MyClass'>
<MagicMock name='MyClass' spec='MyClass' id='4387847136'>
在这种情况下,您只需要导入模块,而不是直接导入类.然后,一旦应用了补丁,您将直接在文件中使用它:
If this is the case you just need to import the module, not the class directly. Then once the patch is applied you will be using it right from the file:
import production_file
...
class Caller:
def bar(self, smth):
print(production_file.MyClass)
obj = production_file.MyClass()
...
class MyTest(unittest.TestCase):
def test_caller(self):
with patch('production_file.MyClass', autospec=MyClass) as MyClassMock:
...
这篇关于python3模拟不适用于所有路径的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!