背景:为什么Java中不建议使用Static函数
这个问题要从测试驱动开发说起。在进行设计前,先考虑如何测试。当模块存在依赖关系,我们通常使用Mock的方式模拟被依赖对象来分离关注点。而在反射技术和Mockito等工具尚未像现在这样成熟之前,测试人员需要手动编写模拟类,这个手动编写的类需要和真实类继承自相同的接口。为了让被依赖的对象是可替换的,这就要求:
- 对象间不存在直接的的依赖关系,而是依赖于共同的抽象接口——高层对象调用低层对象的接口,低层对象实现该接口,使用依赖注入的方式注入被依赖对象。
在Java中,Staic函数难以被替换,因此依赖于Static函数的模块是难以测试的,故留下了这样的传统。
实验:Python是否可以Mock Static?
在Python中,类,甚至模块也是对象,那么使用现有的Pytest-Mock是否可以Mock 一个Static函数呢?下面用一个实验来说明类方法和模块方法两种情况:
类方法
# CM.py
class C:
@staticmethod
def s():
return 0
# tests/test_CM.py
from pytest_mock import MockerFixture
from CM import C,m
import CM
def test_no_mock():
assert C.s() == 0
def test_mock(mocker:MockerFixture):
mocker.patch.object(C,'s',return_value=1)
assert C.s() == 1
可以看出,类的方法被正确Mock了
模块方法
# CM.py
def m():
return 0
# test_CM.py
def test_mockm(mocker:MockerFixture):
assert m()==0
mocker.patch.object(CM,"m",return_value=1)
assert m()==0 # 注意,这里是0,而不是1
assert CM.m()==1
可以看出,在使用模块调用方法时可以被正确Mock,但直接调用的方法的方法的情况下没有正确Mock。
结论:Python中需要注意的问题
从上面实验可以看出,在Pytest-mock中,
- 可以Mock类的static方法,无需顾忌这条规则;
- 也可以Mock模块的方法,但直接调用则无效。需要使用
module_name.method_name
的方法调用。
因此,python编码建议:对于模块方法,不要直接使用from 模块 import 方法
方法名()
来调用,而是import 模块
模块名.方法名()
的方式调用。