2012-09-05 92 views
10

我有一些在python的unittest2框架中運行的基於類的單元測試。我們使用Selenium WebDriver,它有一個方便的save_screenshot()方法。我想在tearDown()中抓取每次測試失敗的截圖,以減少調試時間爲什麼測試失敗。如何僅使用python unittest2對測試失敗執行代碼?

但是,我找不到任何方法只能在測試失敗時運行代碼。不管測試是否成功,調用tearDown(),我都不想使用數百個瀏覽器截圖來爲我們的文件系統混亂,以便測試成功。

你會如何處理這個問題?

回答

6

找到一個解決辦法 - 我可以覆蓋failureException

@property 
def failureException(self): 
    class MyFailureException(AssertionError): 
     def __init__(self_, *args, **kwargs): 
      self.b.save_screenshot('%s.png' % self.id()) 
      return super(MyFailureException, self_).__init__(*args, **kwargs) 
    MyFailureException.__name__ = AssertionError.__name__ 
    return MyFailureException 

這似乎令人難以置信的哈​​克,但它似乎工作至今。

+0

哇,什麼黑客。包裝在一個函數中的異常類包裝在一個屬性中,所有這些都將'TestCase'實例放入異常的'__init __()'方法中。醜,但至少它的作品! – kindall

2

覆寫fail()生成屏幕截圖然後調用TestCase.fail(self)

+1

這隻有在測試用例失敗時纔會有幫助,因爲有人調用self.fail()。其他故障,如'self.assertTrue()',...等繞過這一點。 – craigds

+0

真的嗎?我不能相信他們沒有那些叫'失敗()'。當然,你也可以重寫所有其他方法(嘆氣)。 – kindall

+0

他們中的一些人,但其他人不。似乎有點瘋狂;) – craigds

1

圍繞每個測試使用裝飾器。

要記住裝飾新測試或避免重​​新裝飾一堆現有測試,最安全的方法是使用元類來包裝所有測試函數。 How to wrap every method of a class?答案提供了你所需要的基礎知識。

你或許應該篩選包裹到剛剛測試的功能,例如:

class ScreenshotMetaClass(type): 
    """Wraps all tests with screenshot_on_error""" 
    def __new__(meta, classname, bases, classDict): 
     newClassDict = {} 
     for attributeName, attribute in classDict.items(): 
      if type(attribute) == FunctionType and 'test' in attributeName.lower(): 
       # replace the function with a wrapped version 
       attribute = screenshot_on_error(attribute) 
      newClassDict[attributeName] = attribute 
     return type.__new__(meta, classname, bases, newClassDict) 
2

sys.exc_info()應該給你退出在測試是否失敗與否的信息。因此,像這樣:

def tearDown(self): 
    if sys.exc_info()[0]: 
     path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../failures', self.driver.browser) 
     if not os.path.exists(path): 
      try: 
       os.makedirs(path) 
      except Exception: 
       # Since this might not be thread safe 
       pass 
     filename = '%s.%s.png' % (self.__class__.__name__, self._testMethodName) 
     file_path = os.path.join(path, filename) 
     self.driver.get_screenshot_as_file(file_path) 
+0

'如果sys.exc_info()[0]'在Python 3中不起作用。 – l0b0

2

下面是類似的方法來@craigds answer,但目錄的支持和更好的兼容性與Python 3:

@property 
def failureException(self): 
    class MyFailureException(AssertionError): 
     def __init__(self_, *args, **kwargs): 
      screenshot_dir = 'reports/screenshots' 
      if not os.path.exists(screenshot_dir): 
       os.makedirs(screenshot_dir) 
      self.driver.save_screenshot('{0}/{1}.png'.format(screenshot_dir, self.id())) 
      return super(MyFailureException, self_).__init__(*args, **kwargs) 
    MyFailureException.__name__ = AssertionError.__name__ 
    return MyFailureException 

這本blog竟是找到。

我多用​​進一步擴展它:

parser.add_argument("-r", "--reports-dir", action="store", dest="dir",  help="Directory to save screenshots.", default="reports")  

這樣的目錄可以動態地或者通過系統變量或傳遞的參數指定:

screenshot_dir = os.environ.get('REPORTS_DIR', self.args.dir) + '/screenshots' 

這是特別有用,如果你還有其他的包裝來運行你的所有腳本,比如基類。