unitTest 一个 python 3 元类
unitTest a python 3 metaclass
我有一个 metaclass,它在加载 class 时设置 class 属性 my_new_property
。该文件将我命名为 my_meta
,代码为
def remote_function():
# Get some data from a request to other site
return 'remote_response'
class MyMeta(type):
def __new__(cls, *args, **kwargs):
print("It is in")
obj = super().__new__(cls, *args, **kwargs)
new_value = remote_function()
setattr(obj, 'my_new_property', new_value)
return obj
设置 属性 的功能工作正常,但是在编写测试文件 tests.py
时 只有一个代码行 :
from my_meta import MyMeta
执行元代码。因此,它执行真正的方法 remote_function
.
问题是...由于元代码仅通过使用测试文件中的 import
执行,我如何模拟方法 remote_function
?
按照您向我们展示的方式导入文件不会触发元class代码的执行。
然而,导入任何文件(包括元class所在的文件),其中有一个class利用了这个元class,将 运行 metaclass __new__
方法中的代码 - 因为解析用 class
语句定义的 class 主体就是这样做的: 调用 metaclass 来创建一个新的 class 实例。
因此,建议是:不要使用元class __new__
或 __init__
方法来触发副作用,例如访问远程内容,如果无法做到的话以无缝和无害的方式。不仅是测试,而且在 Python shell 中导入您的应用程序模块也会触发该行为。
你可以在 metaclass 中有一个方法来初始化远程值,当你要实际使用它们时,你会明确地调用这样的 "remote_init" - 就像在
class MyMeta(type):
def __new__(cls, *args, **kwargs):
print("It is in")
obj = super().__new__(cls, *args, **kwargs)
new_value = remote_function()
setattr(obj, 'my_new_property', new_value)
return obj
def remote_init(cls):
if hasattr(cls, "my_new_property"):
return
cls.my_new_property = remote_function()
remote_init 方法,被放置在 metaclass 中,对于实例化的 classes,其行为与 class 方法一样,但不可见(用于目录或属性检索),来自 class 个实例。
这是最安全的做法。
如果你想避免明确的步骤,这是可以理解的,你可以使用配置文件中的设置,并在 remote_function 中测试是否触发实际的网络代码,或者只是 return 一个本地的虚拟值。然后,您使 testing/staging/production.
的配置不同
最后,你可以在另一个模块中分离 remote_method
,首先导入它,用 unitttest.mock.patch
修补它,然后导入包含模块的 metaclass - 当它 运行s 并调用该方法,它将只是 pacthed 版本。这将适用于您的测试,但不会解决您在其他情况下触发 side-effects 的问题(例如在加载此模块的其他测试中)。
当然,要使其工作,您必须在 mock.patch
处于活动状态,而不是在文件顶部导入它。在测试方法中导入东西以控制导入过程本身是没有问题的。
我有一个 metaclass,它在加载 class 时设置 class 属性 my_new_property
。该文件将我命名为 my_meta
,代码为
def remote_function():
# Get some data from a request to other site
return 'remote_response'
class MyMeta(type):
def __new__(cls, *args, **kwargs):
print("It is in")
obj = super().__new__(cls, *args, **kwargs)
new_value = remote_function()
setattr(obj, 'my_new_property', new_value)
return obj
设置 属性 的功能工作正常,但是在编写测试文件 tests.py
时 只有一个代码行 :
from my_meta import MyMeta
执行元代码。因此,它执行真正的方法 remote_function
.
问题是...由于元代码仅通过使用测试文件中的 import
执行,我如何模拟方法 remote_function
?
按照您向我们展示的方式导入文件不会触发元class代码的执行。
然而,导入任何文件(包括元class所在的文件),其中有一个class利用了这个元class,将 运行 metaclass __new__
方法中的代码 - 因为解析用 class
语句定义的 class 主体就是这样做的: 调用 metaclass 来创建一个新的 class 实例。
因此,建议是:不要使用元class __new__
或 __init__
方法来触发副作用,例如访问远程内容,如果无法做到的话以无缝和无害的方式。不仅是测试,而且在 Python shell 中导入您的应用程序模块也会触发该行为。
你可以在 metaclass 中有一个方法来初始化远程值,当你要实际使用它们时,你会明确地调用这样的 "remote_init" - 就像在
class MyMeta(type):
def __new__(cls, *args, **kwargs):
print("It is in")
obj = super().__new__(cls, *args, **kwargs)
new_value = remote_function()
setattr(obj, 'my_new_property', new_value)
return obj
def remote_init(cls):
if hasattr(cls, "my_new_property"):
return
cls.my_new_property = remote_function()
remote_init 方法,被放置在 metaclass 中,对于实例化的 classes,其行为与 class 方法一样,但不可见(用于目录或属性检索),来自 class 个实例。 这是最安全的做法。
如果你想避免明确的步骤,这是可以理解的,你可以使用配置文件中的设置,并在 remote_function 中测试是否触发实际的网络代码,或者只是 return 一个本地的虚拟值。然后,您使 testing/staging/production.
的配置不同最后,你可以在另一个模块中分离 remote_method
,首先导入它,用 unitttest.mock.patch
修补它,然后导入包含模块的 metaclass - 当它 运行s 并调用该方法,它将只是 pacthed 版本。这将适用于您的测试,但不会解决您在其他情况下触发 side-effects 的问题(例如在加载此模块的其他测试中)。
当然,要使其工作,您必须在 mock.patch
处于活动状态,而不是在文件顶部导入它。在测试方法中导入东西以控制导入过程本身是没有问题的。