Mockh模块不起作用问题

问题:

最近的单元测试中,使用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

    此方式引入模块时,执行的操作是:

    1. 创建新的命名空间,用作在相应源文件中定义的所有对象的容器。在模块重定义的函数和方法在使用global语句时将访问该命名空间。
    2. 在新创建的命名空间中执行模块中包含的代码。
    3. 在调用函数中创建名称来引用模块命名空间。这个名称与模块的名称相匹配。
  • from module import func

    使用此方式引入方法时,将模块中的具体定义加载到当前命名空间中。from语句相当于import,但它不会创建一个名称来引用新创建的模块命名空间,而是将对模块中定义的一个或多个对象的引用放到当前命名空间中

所以这两种引入造成的区别是命名空间的不同,因此import的不同结果可能就是命名空间造成的。可以做出如下的解释:

  1. 采用import module 引入时,会根据原始action模块创建命名空间,调用my_func() 方法时,是在原始的命名空间下执行的,而mock中的patch() 方法使用的就是原始action模块对应命名空间,因此这种方式是可以正常模拟的。
  2. 采用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/

 

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值