2016-11-29 64 views
1
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

我想你現在問「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) 
+0

好吧,這是有道理的,但爲什麼測試通過首先:D。我的意思是它不應該。 – Krotz

+1

您是否有可能在裝修工廠之後和何時不把括號放在後面而感到困惑?您可能已經創建了一個實際上只是裝飾器功能的測試用例,例如'def decorator_factory(* args,** kwargs):... return decorator',然後是'@decorator_factory def test_method(self):self.assertEqual(True,False)',因此這裏的裝飾器工廠用'test_method'調用並返回一個用作測試用例的裝飾器,但它只是創建一個包裝測試的新函數,並且實際上並不運行測試,所以看起來會通過。 – daphtdazz

+0

無論是運行測試還是運行測試時,它都會捕獲斷言錯誤,並將失敗的測試存儲在基於測試函數__name__的字典中,該測試函數__name__不在字典中,因此它忽略它。無論如何,我從本質上得到了它的要義,謝謝。 – Krotz