9

这里是龙。你已经被警告过了。

我正在考虑创建一个新的库来帮助编写更好的测试套件。
为了做到这一点,其中一个功能是验证正在使用的任何不是测试运行器和被测系统的对象是否具有测试替身(模拟对象、存根、假对象或虚拟对象) )。如果测试人员想要活动对象并因此减少测试隔离,则必须如此明确地指定。

我看到这样做的唯一方法是覆盖type()默认元类的内置函数。
新的默认元类将检查测试替身注册表字典以查看它是否已被测试替身替换或是否指定了活动对象。

当然,这通过 Python 本身是不可能的:

>>> TypeError: can't set attributes of built-in/extension type 'type'

有没有办法在测试套件运行(可能是 Python)之前干预 Python 的元类查找?
也许使用字节码操作?但具体如何?

4

2 回答 2

9

以下是不可取的,您会遇到很多问题和实现您的想法的极端情况,但是在 Python 3.1 及更高版本上,您可以通过覆盖内置挂钩来挂钩自定义类创建过程:__build_class__

import builtins


_orig_build_class = builtins.__build_class__


class SomeMockingMeta(type):
    # whatever


def my_build_class(func, name, *bases, **kwargs):
    if not any(isinstance(b, type) for b in bases):
        # a 'regular' class, not a metaclass
        if 'metaclass' in kwargs:
            if not isinstance(kwargs['metaclass'], type):
                # the metaclass is a callable, but not a class
                orig_meta = kwargs.pop('metaclass')
                class HookedMeta(SomeMockingMeta):
                    def __new__(meta, name, bases, attrs):
                        return orig_meta(name, bases, attrs)
                kwargs['metaclass'] = HookedMeta
            else:
                # There already is a metaclass, insert ours and hope for the best
                class SubclassedMeta(SomeMockingMeta, kwargs['metaclass']):
                    pass
                kwargs['metaclass'] = SubclassedMeta
        else:
            kwargs['metaclass'] = SomeMockingMeta

    return _orig_build_class(func, name, *bases, **kwargs)


builtins.__build_class__ = my_build_class

这仅限于自定义类,确实为您提供了一个全能的钩子。

对于 3.1 之前的 Python 版本,您可以忘记挂钩类的创建。如果没有定义元类,Cbuild_class函数直接使用 C 类型的值,它从不从模块中查找它,因此您不能覆盖它。type()__builtin__

于 2013-03-13T17:19:07.580 回答
2

我喜欢你的想法,但我认为你会稍微偏离正轨。如果代码调用库函数而不是类怎么办?您的假 type() 永远不会被调用,并且您永远不会被告知您未能模拟该库函数。在 Django 和任何真实的代码库中都有很多实用功能。

我建议您以 Python 源代码补丁的形式编写所需的解释器级支持。或者,您可能会发现将这样的钩子添加到 PyPy 的代码库中更容易,该代码库是用 Python 本身编写的,而不是弄乱 Python 的 C 源代码。

我刚刚意识到 Python 解释器包含一套全面的工具,可以让任何 Python 代码单步执行任何其他代码,检查它对每个函数调用的作用,甚至检查每个单独的 Python 行如果需要,执行。

sys.setprofile应该足以满足您的需求。有了它,您可以安装一个挂钩(回调),该挂钩将收到目标程序进行的每个函数调用的通知。您不能使用它来更改目标程序的行为,但您可以收集有关它的统计信息,包括您的“模拟覆盖率”指标。

Python 关于 Profilers 的文档介绍了许多基于sys.setprofile. 您可以研究他们的资源以了解如何有效地使用它。

如果这还不够,仍然有sys.settrace一种严厉的方法,可以让您单步执行目标程序的每一行,检查其变量并修改其执行。标准模块bdb.py建立在sys.settrace并实现标准的调试工具集(断点、单步执行、单步跳过等)。pdb.py命令行调试器和其他图形调试器都使用它。

有了这两个钩子,你应该没事。

于 2013-03-14T22:31:13.640 回答