Decorator工厂采用unittest方法

时间:2016-11-29 13:03:09

标签: python python-unittest python-decorators

def register_processor2(processor_name='SomeProcessor'):
    def decorator(func):
        class SomeProcessor(GenericPaymentProcessor, TriggeredProcessorMixin):
            name = processor_name
            transaction_class = Transaction

            @staticmethod
            def setup(data=None):
                pass

        @wraps(func)
        def func_wrapper(*args, **kwargs):
            PaymentProcessorManager.register(SomeProcessor)
            result = func(*args, **kwargs)
            PaymentProcessorManager.unregister(SomeProcessor)
            return result

        return func_wrapper
    return decorator


def register_processor(func):
    class SomeProcessor(GenericPaymentProcessor, TriggeredProcessorMixin):
         name = 'SomeProcessor'
         transaction_class = Transaction

         @staticmethod
         def setup(data=None):
             pass

    @wraps(func)
    def func_wrapper(*args, **kwargs):
        PaymentProcessorManager.register(SomeProcessor)
        result = func(*args, **kwargs)
        PaymentProcessorManager.unregister(SomeProcessor)
        return result

    return func_wrapper


class TestPaymentMethodEndpoints(APITestCase):
    @register_processor
    def test_put_detail_cannot_change_processor(self):
        self.assertEqual(True, False)

好的,所以装饰器register_processor按预期工作。测试失败,但我想让内部类的名称可自定义,所以我去了装饰工厂实现。

当运行用register_processor2装饰的测试时,我得到以下内容:

AttributeError: 'TestPaymentMethodEndpoints' object has no attribute '__name__'

这是@wraps(func),我的问题是为什么func这里是TestPaymentMethodEndpoints的一个实例,而不是绑定的方法?

此外,如果我移除@wraps装饰器,则测试运行并通过。 我预计不会发现测试,因为func_wrapper不会以test_*开头,即使它被发现也会失败。

有关正在发生的事情以及我将如何进行此操作的任何见解?

修改

因此,即使装饰工厂的参数具有默认值,您仍然需要在调用它时放置()

但是,我仍然希望听到有关测试通过/首先被发现时发生的事情的解释。

class TestPaymentMethodEndpoints(APITestCase):
    @register_processor()
    def test_put_detail_cannot_change_processor(self):
        self.assertEqual(True, False)

现在我觉得有意义:D,你每天都学到新的东西!

1 个答案:

答案 0 :(得分:1)

我想你现在问“unittest模块怎么能找到包含在名称不能启动test的函数中的测试用例?”

答案是因为unittest不使用函数的名称来查找要运行的方法,它使用属性名称测试用例类来查找它们。

因此,请尝试运行以下代码:

from unittest import TestCase

def apply_fixture(func):

    def wrap_with_fixture(self):
        print('setting up fixture...')
        try:
            func(self)
        finally:
            print('tearing down fixture')

    return wrap_with_fixture


class MyTestCase(TestCase):

    @apply_fixture
    def test_something(self):
        print('run test')


print('Attributes of MyTestCase: %s' % dir(MyTestCase))
print('test_something method: %s' % MyTestCase.test_something)

mtc = MyTestCase()
mtc.test_something()

您会看到dir的输出包含名称test_something

Attributes of MyTestCase: ['__call__', ...lots of things..., 'test_something']

但该属性的值是包装函数wrap_with_fixture

test_something method: <function apply_fixture.<locals>.wrap_with_fixture at 0x10d90aea0>

当你考虑创建一个函数时,你正在创建一个具有所提供名称的函数和一个具有相同名称的局部变量,并且装饰器@语法只是语法糖,这是有意义的,所以以下是创建测试用例类的同样有效但尽管冗长的方法:

class MyTestCase(TestCase):

    def test_something(self):
        print('run test')
    # Overwrite existing 'local' (or 'class' variable in this context) 
    # with a new value. We haven't deleted the test_something function
    # which still exists but now is owned by the function we've created.
    test_something = apply_fixture(test_something)