概述
问题:
最近的单元测试中,使用Mock模块时发现一个一个奇怪的问题,当使用from module import func
后,如果使用Mock去模拟对应的方法时,执行的依旧是原始方法,而不是模拟的方法。下面使用代码解释一下:
在项目中存在三个python文件,其中一个是action.py
文件,代码为:
def func():
return 'original func'
第二个是execute_to_action.py
文件,其代码文件为:
from action import func
def my_func():
return func()
另一个是test.py
文件,其代码文件为:
import unittest
from mock import patch
from execute_to_action import my_func
class MyTestCase(unittest.TestCase):
@patch('action.func')
def test_func(self, mock_func):
mock_func.return_value = 'new value'
result = my_func()
print(result)
if __name__ == '__main__':
unittest.main()
使用pytest执行上面的测试代码,最终执行的结果是original func
,说明mock没有成功
但是采用另一种引入方式,将action.py
修改为如下所示:
import action
def my_func():
return action.func()
执行结果是new value
,说明这种方式是可以正常mock成功的。
import
按照之前使用的情况来看,import module
和from module import func
是类似,但是从mock的情况来看,这两种使用方式存在一些细微的区别,那么区别到底是什么呢?
一般搜索之后,这两种方式执行的操作是不同的。
-
import module
此方式引入模块时,执行的操作是:
- 创建新的命名空间,用作在相应源文件中定义的所有对象的容器。在模块重定义的函数和方法在使用global语句时将访问该命名空间。
- 在新创建的命名空间中执行模块中包含的代码。
- 在调用函数中创建名称来引用模块命名空间。这个名称与模块的名称相匹配。
-
from module import func
使用此方式引入方法时,将模块中的具体定义加载到当前命名空间中。from语句相当于import,但它不会创建一个名称来引用新创建的模块命名空间,而是将对模块中定义的一个或多个对象的引用放到当前命名空间中
所以这两种引入造成的区别是命名空间的不同,因此import的不同结果可能就是命名空间造成的。可以做出如下的解释:
- 采用
import module
引入时,会根据原始action模块创建命名空间,调用my_func()
方法时,是在原始的命名空间下执行的,而mock中的patch()
方法使用的就是原始action模块对应命名空间,因此这种方式是可以正常模拟的。 - 采用
from module import func
的方式引入时,会将原始方法加载到当前test模块的命名空间中,而mock中的patch()
方法中使用的还是原始action模块对应的命名空间,因此这种方式不能正确模拟方法。
那么如何验证这种猜测呢,既然from module import func
是将原始方法加载到当前test模块的命名空间中,可以在mock的patch()
方法中模拟的当前test模块的my_func()
方法,应该就可以正常工作了。那么将execute_to_action.py
恢复原始状态,test.py
的代码修改为如下所示:
import unittest
from mock import patch
from execute_to_action import my_func
class MyTestCase(unittest.TestCase):
@patch('execute_to_action.func')
def test_func(self, mock_func):
mock_func.return_value = 'new value'
result = my_func()
print(result)
if __name__ == '__main__':
unittest.main()
测试结果是new value
,说明猜测正确
结论
这个问题还是对python的引入机制了解不够深入造成的。下面详细介绍一下:
- 采用
import module
引入时,会创建新的命名空间,此命名空间与模块的名称一致,最终访问引入模块上的方法时,访问的是原始模块对应的路径,因此这种方式引入时,上面的func()
方法对应的命名空间为execute_to_action。
- 采用
from module import func
引入时,不会创建新的命名空间,会将引入的方法加载到当前命名空间中。这样访问引入模块的方法时,访问的是当前模块的路径。因此这种方式引入时,上面的func()
方法对应的命名空间为action。
参考:
https://hustyichi.github.io/2018/12/13/mock-and-module-import/
最后
以上就是勤劳大碗为你收集整理的Mockh模块不起作用问题的全部内容,希望文章能够帮你解决Mockh模块不起作用问题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复