2

当我调用 mock.patch 时,我希望它用我使用 new 关键字参数提供的类型替换我正在替换的类型。
它不会替换类型,但在patch.start()调用时会返回正确的对象。
FakesPatcher是一种强制旧对象创建新对象的技巧。它适用于Python 3.xPyPy但是,它不适用于 Python 2.x(见下面的编辑)。无论如何,
我希望它消失并使用。FakesPatchermock.patch

我在这里做错了什么,我该如何解决?

def substitute(obj, qualified_name, spec):
    testdouble = mock.patch(qualified_name, spec=spec, spec_set=True, new=obj)
    testdouble.attribute_name = qualified_name # Forces patch to use the extra patcher

    class FakesPatcher(object):
        """Ugly hack."""
        new = 1

        def _new(*args, **kwargs): 
            return obj.__new__(obj)

        def __enter__(self):
            self._old_new = spec.__new__
            spec.__new__ = self._new
            return obj

        def __exit__(self, exc_type, exc_val, exc_tb):
            spec.__new__ = self._old_new

    testdouble.additional_patchers.append(FakesPatcher())

    return testdouble


def fake(obj):
   """


   :rtype : mock._patch
   :param obj:
   """
    try:
        configuration = obj.Configuration()
    except AttributeError:
        raise TypeError('A fake testdouble must have a Configuration class.')

    try:
        spec = configuration.spec
    except AttributeError:
        raise TestDoubleConfigurationError('The type to be faked was not specified.')

    qualified_name = get_qualified_name(spec)

    attrs = dict(obj.__dict__)
    attrs.pop('Configuration')

    methods = get_missing_methods(spec, obj)
    for method in methods:
        def make_default_implementation(attr):
            def default_implementation(*args, **kwargs):
                raise NotImplementedError('%s was not implemented when the object was faked.' % attr)

            return default_implementation

        attrs.update({method: make_default_implementation(method)})

    properties = get_missing_properties(spec, obj)
    for prop in properties:
        def make_default_implementation(attr):
            def default_implementation(*args, **kwargs):
                raise NotImplementedError('%s was not implemented when the object was faked.' % attr)

            return property(fget=lambda *args, **kwargs: default_implementation(*args, **kwargs),
                            fset=lambda *args, **kwargs: default_implementation(*args, **kwargs),
                            fdel=lambda *args, **kwargs: default_implementation(*args, **kwargs))

        attrs.update({prop: make_default_implementation(prop)})

    fake_qualified_name = get_qualified_name(obj)
    obj = type(obj.__name__, obj.__bases__, attrs)

    return substitute(obj, qualified_name, spec)

如果您想使用代码并对其进行测试,您可以在此处找到它。

编辑:
我通过用实例方法替换 lambda解决了 Python 2.x 错误。

4

1 回答 1

1

在您的测试中,如果您想mock.patchwith语句中使用,mock 库要求您使用补丁的返回值作为 mock 对象。你的测试现在变成

    @it.should('replace the original methods with the fake methods')
    def test_should_replace_the_original_methods_with_the_fake_methods(case):
        class FakeObject(object):
            class Configuration(object):
                spec = RealObject

            def was_faked(self):
                return True

        with fake(FakeObject) as realObject:
            fake_obj = realObject()

            case.assertTrue(fake_obj.was_faked())

然后,您可以使用以下替代品,甚至摆脱它。

def substitute(obj, qualified_name, spec):
    return mock.patch(qualified_name, new=obj, spec=spec)

修补通过在调用站点修补类型来工作。以下文档摘录很重要。

目标应该是格式为“package.module.ClassName”的字符串。导入目标并将指定对象替换为新对象,因此目标必须可以从您调用补丁的环境中导入。目标是在执行装饰函数时导入的,而不是在装饰时。

如果要修补实际类型,而不使用with语句的返回值,则不能将类的名称解析为限定名称,而是本地名称。

以下变化

    @it.should('replace the original methods with the fake methods')
    def test_should_replace_the_original_methods_with_the_fake_methods(case):
       ...
    with fake(FakeObject, '%s.%s' % (__name__,'RealObject')):
        fake_obj = RealObject()

        case.assertTrue(fake_obj.was_faked())

testdoubles__init__.py

def fake(obj, qualified_name=None):
    """
    :rtype : mock._patch
    :param obj:
    """
    try:
        configuration = obj.Configuration()
    except AttributeError:
        raise TypeError('A fake testdouble must have a Configuration class.')

    try:
        spec = configuration.spec
    except AttributeError:
        raise TestDoubleConfigurationError('The type to be faked was not specified.')

    qualified_name = qualified_name or get_qualified_name(spec)
    ...

现在的问题是您无法可靠地找出 RealObject 的来源,至少我无法真正找到方法。您可以假设它来自调用函数所在的模块并执行以下操作:

    qualified_name = "%s.%s" % (obj.__module__, spec.__name__)
于 2013-10-09T18:10:23.517 回答