本篇博文參考了簡書博文,原文鏈接:https://www.jianshu.com/p/2c471acfea2e
當我們自動化用例多的時候,經常會有部分用例因為各種各樣的原因跑失敗,但其實應用本身是沒有問題的,所以,加入失敗用例重跑機制,如果第二次運行成功,那么我們就認為這條用例是通過的,同時,測試報告上面寫的也是通過
這里介紹兩種方法。一種是函數裝飾器,一種是類裝飾器。兩者功能相同
函數裝飾器
# -*- coding = UTF-8 -*-
# Autohr : 葉松橋
# File : runfailed.py
# project : Test_Ui_Yt
# time : 2020/11/27 12:21
# Describe : 失敗用例重跑
# ---------------------------------------
import sys
import functools
import traceback
import inspect
import unittest
def retry(target=None, max_n=1, func_prefix="test"):
"""
一個裝飾器,用於unittest執行測試用例出現失敗后,自動重試執行
# example_1: test_001默認重試1次
class ClassA(unittest.TestCase):
@retry
def test_001(self):
raise AttributeError
# example_2: max_n=2,test_001重試2次
class ClassB(unittest.TestCase):
@retry(max_n=2)
def test_001(self):
raise AttributeError
# example_3: test_001重試3次; test_002重試3次
@retry(max_n=3)
class ClassC(unittest.TestCase):
def test_001(self):
raise AttributeError
def test_002(self):
raise AttributeError
# example_4: test_102重試2次, test_001不參與重試機制
@retry(max_n=2, func_prefix="test_1")
class ClassD(unittest.TestCase):
def test_001(self):
raise AttributeError
def test_102(self):
raise AttributeError
:param target: 被裝飾的對象,可以是class, function
:param max_n: 重試次數,沒有包含必須有的第一次執行
:param func_prefix: 當裝飾class時,可以用於標記哪些測試方法會被自動裝飾
:return: wrapped class 或 wrapped function
"""
def decorator(func_or_cls):
if inspect.isfunction(func_or_cls):
@functools.wraps(func_or_cls)
def wrapper(*args, **kwargs):
n = 0
while n <= max_n:
try:
n += 1
func_or_cls(*args, **kwargs)
return
except Exception: # 可以修改要捕獲的異常類型
if n <= max_n:
trace = sys.exc_info()
traceback_info = str()
for trace_line in traceback.format_exception(trace[0], trace[1], trace[2], 3):
traceback_info += trace_line
print(traceback_info) # 輸出組裝的錯誤信息
args[0].tearDown()
args[0].setUp()
else:
raise
return wrapper
elif inspect.isclass(func_or_cls):
for name, func in list(func_or_cls.__dict__.items()):
if inspect.isfunction(func) and name.startswith(func_prefix):
setattr(func_or_cls, name, decorator(func))
return func_or_cls
else:
raise AttributeError
if target:
return decorator(target)
else:
return decorator
類裝飾器
# -*- coding = UTF-8 -*-
# Autohr : 葉松橋
# File : runfailed.py
# project : Test_Ui_Yt
# time : 2020/11/27 12:21
# Describe : 失敗用例重跑
# ---------------------------------------
import sys
import functools
import traceback
import inspect
import unittest
class Retry(object):
"""
類裝飾器, 功能與Retry一樣
# example_1: test_001默認重試1次
class ClassA(unittest.TestCase):
@Retry
def test_001(self):
raise AttributeError
# example_2: max_n=2,test_001重試2次
class ClassB(unittest.TestCase):
@Retry(max_n=2)
def test_001(self):
raise AttributeError
# example_3: test_001重試3次; test_002重試3次
@Retry(max_n=3)
class ClassC(unittest.TestCase):
def test_001(self):
raise AttributeError
def test_002(self):
raise AttributeError
# example_4: test_102重試2次, test_001不參與重試機制
@Retry(max_n=2, func_prefix="test_1")
class ClassD(unittest.TestCase):
def test_001(self):
raise AttributeError
def test_102(self):
raise AttributeError
"""
def __new__(cls, func_or_cls=None, max_n=1, func_prefix="test"):
self = object.__new__(cls)
if func_or_cls:
self.__init__(func_or_cls, max_n, func_prefix)
return self(func_or_cls)
else:
return self
def __init__(self, func_or_cls=None, max_n=1, func_prefix="test"):
self._prefix = func_prefix
self._max_n = max_n
def __call__(self, func_or_cls=None):
if inspect.isfunction(func_or_cls):
@functools.wraps(func_or_cls)
def wrapper(*args, **kwargs):
n = 0
while n <= self._max_n:
try:
n += 1
func_or_cls(*args, **kwargs)
return
except Exception: # 可以修改要捕獲的異常類型
if n <= self._max_n:
trace = sys.exc_info()
traceback_info = str()
for trace_line in traceback.format_exception(trace[0], trace[1], trace[2], 3):
traceback_info += trace_line
print(traceback_info) # 輸出組裝的錯誤信息
args[0].tearDown()
args[0].setUp()
else:
raise
return wrapper
elif inspect.isclass(func_or_cls):
for name, func in list(func_or_cls.__dict__.items()):
if inspect.isfunction(func) and name.startswith(self._prefix):
setattr(func_or_cls, name, self(func))
return func_or_cls
else:
raise AttributeError
運行效果如圖,失敗的用例會再次運行2遍

