2015-11-25 35 views
0

我希望能夠寫一些測試中類似這樣的格式:pytest發現缺失的裝飾方法

class TestPytest: 
    @given(3) 
    @expect(3) 
    def test_passes(self, g, e): 
     assert g == e 

    @given(3) 
    @expect(4) 
    def test_fails(self, g, e): 
     assert g == e 

    def test_boring(self): # for comparison 
     pass 

(我不認爲這是一個好主意,但我會。把它進一步的方向,所以它不是奇怪,因爲它看起來)

爲此,我已經嘗試寫這些裝飾:

import functools 

class WrappedTest(object): 
    def __init__(self, f): 
     self.func = f 
     self.given = [] 
     self.expects = [] 

    def __get__(self, instance, owner): 
     @functools.wraps(self.func) 
     def call(*a, **kw): 
      return self.func(instance, self.given, self.expects, 
          *a, **kw) 
     return call 

def given(*objects): 
    def wrapped(test): 
     if not isinstance(test, WrappedTest): 
      test_tmp = WrappedTest(test) 
      test = functools.update_wrapper(test_tmp, test) 

     test.given.extend(objects) 
     return test 
    return wrapped 

def expect(*objects): 
    def wrapped(test): 
     if not isinstance(test, WrappedTest): 
      test_tmp = WrappedTest(test) 
      test = functools.update_wrapper(test_tmp, test) 

     test.expects.extend(objects) 
     return test 
    return wrapped 

但是當我嘗試運行這個測試,pytest沒有找到test_passestest_fails。它確實找到test_boring

我的工作假設是我沒有正確包裝測試方法。他們顯示爲功能,而不是方法:

>>> test_pytest.TestPytest().test_fails 
<function test_pytest.test_fails> 

>>> test_pytest.TestPytest().test_boring 
<bound method TestPytest.test_boring of <test_pytest.TestPytest instance at 0x101f3dab8>> 

但我不知道如何解決這個問題。我試過將functools.wraps(self.func)更改爲functools.wraps(self.func.__get__(instance, owner)),理論上它會用綁定方法而不是函數進行包裝。但這是一種猜測,並沒有奏效。

我知道pytest能夠找到正確編寫的裝飾函數,所以大概我做錯了什麼,但我不知道是什麼。

回答

0

它看起來像我錯了關於包裝。通過pytest源代碼,它將嵌套類與方法區別開來。它通過__dict__訪問成員,忽略__get__,所以WrappedTest沒有成功假裝成一種方法。

我已經取代了WrappedTest實例與功能,它似乎是工作的罰款(即使沒有@functools.wraps線):

import functools 
from collections import namedtuple 

def wrap_test_method(meth): 
    if hasattr(meth, '_storage'): 
     return meth 

    Storage = namedtuple('Storage', ['given', 'expects']) 
    sto = Storage(given=[], expects=[]) 

    @functools.wraps(meth) 
    def new_meth(self, *a, **kw): 
     return meth(self, sto.given, sto.expects, *a, **kw) 
    new_meth._storage = sto 

    return new_meth 

def given(*objects): 
    def decorator(test_method): 
     new_test_method = wrap_test_method(test_method) 
     new_test_method._storage.given.extend(objects) 
     return new_test_method 

    return decorator 

def expect(*objects): 
    def decorator(test_method): 
     new_test_method = wrap_test_method(test_method) 
     new_test_method._storage.expects.extend(objects) 
     return new_test_method 

    return decorator