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 处于活动状态,而不是在文件顶部导入它。在测试方法中导入东西以控制导入过程本身是没有问题的。