【pytest】如何使用 pytest-rerunfailures 外掛並自定義重跑操作
【pytest】如何使用 pytest-rerunfailures 外掛並自定義重跑操作
作用介紹
pytest-rerunfailures 外掛可以使測試用例在執行測試失敗的時候重新執行,並且會執行測試用例相對應的 setup 和 teardown 方法。通常有測試用例失敗重跑需求的時候就可以使用這個外掛。
但是 rerunfailures 外掛無法控制用例在失敗重跑時進行一些額外操作,這對於有一些自定義需求的業務場景來說很不方便,因此本文主要來介紹如何配合 rerunfailures 外掛來進行一些自定義操作。
外掛下載:
pip install pytest-rerunfailures
使用方法
- 命令列引數方法
- 裝飾器註解方法
命令列引數方法
pytest --reruns 3
只需要在 pytest 的命令列後加上--reruns num
即可,num 的值代表測試失敗時重跑的次數
裝飾器註解方法
@pytest.mark.flaky(reruns=5)
def test_complete_inspect_control():
assert 1 == True
在測試用例上新增@pytest.mark.flaky(reruns=num)
裝飾器,num 的值代表測試失敗時重跑的次數
自定義重跑配置
rerunfailures 裝飾器在測試失敗的時候會固定執行用例的 setup、call、teardown 方法,其中 call 方法代表用例本身的執行函式。
因此我們想要自定義用例重跑時的一些操作,可以從兩個方面入手。
分別是:
- setup、teardown 方法
- rerunfailures 原始碼
setup、teardown 方法
由於 setup 和 teardown 方法在每一個測試用例執行時都會執行一遍,而如果我們的自定義操作只針對於失敗重跑的用例,則需要在 setup 和 teardown 方法里加一些判斷條件。
我的方法是在測試用例的 class 物件中初始化一個 bool 變數,用來標誌當前的測試用例是否需要進行重跑時的自定義操作。即:
class TestCase:
restart_app = False
這裡將 bool 變數命名為 restart_app,因為我的自定義操作是在失敗時重啟 app。
將這個變數初始化為 False,表示預設情況下當前執行的測試用例是不需要執行自定義操作的。
定義好了變數後,就該編寫 setup 函式里的自定義操作的邏輯了。setup 具體邏輯如下:
class TestCaseBase:
restart_app = False
def setup_method(self):
if self.restart_app:
# 這裡想要自定義的操作
self.restart_app = Flase # 失敗重跑時執行完自定義操作後需要將標誌復原
接下來還需要控制這個 restar_app 變數在什麼時候變為 True。
pytest 框架有一個鉤子函式,名為pytest_runtest_makereport
。在這個函式里,每一個執行完畢的測試用例,不論是否成功,都可以在這個函式里獲取他的測試結果,因此可以使用這個鉤子函式來控制標誌的值。
# conftest.py
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
result = yield
rep = result.get_result()
# 獲取setup、call、teardown方法的執行失敗的結果
if rep.when in ("call", "setup", "teardown") and rep.failed:
# 從報告中獲取失敗的詳細資訊
failure_reason = rep.longrepr.reprcrash.message
# 獲取失敗的測試用例名稱
test_name = item.name
# 列印或記錄失敗資訊
logger.error(f"Test '{test_name}' failed with reason: {failure_reason}")
if item.cls is not None: # 確保測試用例屬於某個類
item.cls.restart_app = True # 設定重啟標誌為True
到這一步就大功告成了,每一次測試用例執行失敗的時候,就會在pytest_runtest_makereport
鉤子函式中捕獲到相應的資訊,並將該用例的父類的標誌物件設定為 True。接下來 rerunfailures 外掛就會進行重新執行該用例的 setup、call、teardown 函式。執行 setup 函式時,判斷到 restart_app 的值為 True,就會執行我們設定的自定義操作,執行完畢後將 restart_app 的值恢復為預設值。如此便實現了配合 rerunfailures 外掛執行自定義重跑操作。
修改 rerunfailures 原始碼
在一些需要高度自定義的需求下,單單隻配置 setup、teardown 方法可能不夠用,因此我們可以修改 rerunfailures 的原始碼來實現我們想要的效果。
首先我們要找到 rerunfailures 的原始碼檔案。在 python 虛擬環境中的路徑為:
.venv/Lib/site-packages/pytest_rerunfailures.py
如果使用的是 python 的全域性環境,只需要在 python 的安裝資料夾下的 lib 庫裡尋找即可。
開啟原始檔,找到pytest_runtest_protocol
函式,這個函式也是 pytest 框架的鉤子函式,不過我們沒有必要自己使用這個鉤子函式實現一些重跑邏輯,直接在現有的輪子上做增量即可。
這個函式的原始碼如下:
def pytest_runtest_protocol(item, nextitem):
"""
Run the test protocol.
Note: when teardown fails, two reports are generated for the case, one for
the test case and the other for the teardown error.
"""
reruns = get_reruns_count(item)
if reruns is None:
# global setting is not specified, and this test is not marked with
# flaky
return
# while this doesn't need to be run with every item, it will fail on the
# first item if necessary
check_options(item.session.config)
delay = get_reruns_delay(item)
parallel = not is_master(item.config)
db = item.session.config.failures_db
item.execution_count = db.get_test_failures(item.nodeid)
db.set_test_reruns(item.nodeid, reruns)
if item.execution_count > reruns:
return True
need_to_run = True
while need_to_run:
item.execution_count += 1
item.ihook.pytest_runtest_logstart(nodeid=item.nodeid, location=item.location)
reports = runtestprotocol(item, nextitem=nextitem, log=False)
for report in reports: # 3 reports: setup, call, teardown
report.rerun = item.execution_count - 1
if _should_not_rerun(item, report, reruns):
# last run or no failure detected, log normally
item.ihook.pytest_runtest_logreport(report=report)
else:
# failure detected and reruns not exhausted, since i < reruns
report.outcome = "rerun"
time.sleep(delay)
if not parallel or works_with_current_xdist():
# will rerun test, log intermediate result
item.ihook.pytest_runtest_logreport(report=report)
# cleanin item's cashed results from any level of setups
_remove_cached_results_from_failed_fixtures(item)
_remove_failed_setup_state_from_session(item)
break # trigger rerun
else:
need_to_run = False
item.ihook.pytest_runtest_logfinish(nodeid=item.nodeid, location=item.location)
return True
整段程式碼看起來很長,但是我們只需要重點關注兩個地方:runtestprotocol
和for report in reports
。
runtestprotocol
函式是控制整個測試用例執行 setup、call、teardown 方法的。
而for report in reports
則是針對三個方法來分別識別其執行結果的,我們的一些自定義邏輯基本就寫在這個迴圈下面。
通常來說,如果我們需要在測試用例執行前(setup、call、teardwon)執行一些操作的話,則可以在runtestprotocol
函式前執行,如:
# do something special
reports = runtestprotocol(item, nextitem=nextitem, log=False)
如果需要在測試用例執行結束後,獲取執行結果並進行操作的話,則需要在for report in reports
迴圈下進行操作,如:
for report in reports: # 3 reports: setup, call, teardown
if report.outcome = "failed" :
# do something special
break
report.rerun = item.execution_count - 1
if _should_not_rerun(item, report, reruns):
# last run or no failure detected, log normally
item.ihook.pytest_runtest_logreport(report=report)
else:
# failure detected and reruns not exhausted, since i < reruns
report.outcome = "rerun"
time.sleep(delay)
if not parallel or works_with_current_xdist():
# will rerun test, log intermediate result
item.ihook.pytest_runtest_logreport(report=report)
# cleanin item's cashed results from any level of setups
_remove_cached_results_from_failed_fixtures(item)
_remove_failed_setup_state_from_session(item)
break # trigger rerun
else:
need_to_run = False
這裡只是加了一個無論設定了多少次重跑次數,只要執行失敗(setup、call、teardown)都重新執行的邏輯。
通常大部分的需求會有一些更多的、額外的一些邏輯,需要大家自行處理。
相關文章
- pytest-rerunfailures/pytest-repeat重跑外掛AI
- 關於 pytest 外掛 pytest-rerunfailures 和 fixture 的相容性問題AI
- APISIX Ingress 如何支援自定義外掛API
- gradle自定義外掛Gradle
- mybatis 自定義外掛MyBatis
- Apache Maven Assembly自定義打包外掛的使用ApacheMaven
- vue自定義全域性元件(或自定義外掛)Vue元件
- 如何在Android平臺上建立自定義的Cordova外掛並使用SAP UI5消費AndroidUI
- 自定義Gradle-Plugin 外掛GradlePlugin
- apisix~自定義外掛的部署API
- es 自定義分詞外掛分詞
- Cordova學習--iOS自定義外掛iOS
- 【django學習-24】自定義外掛Django
- gradle自定義任務和外掛Gradle
- Kube-Scheduler外掛的自定義
- 【Maven實戰技巧】「外掛使用專題」Maven-Assembly外掛實現自定義打包Maven
- 如何使用 Ansible 配置 Vim並安裝外掛
- 5分鐘教你使用idea除錯SeaTunnel自定義外掛Idea除錯
- Pytest系列(八) - 自定義標記mark的使用
- 快速自定義Cordova外掛(-配置檔案)
- apisix-dashboard上新增自定義外掛API
- 二 阿里大模型接入:自定義外掛阿里大模型
- mybaits原始碼分析--自定義外掛(七)AI原始碼
- pytest 內建和自定義 marker
- [-Flutter外掛篇 1-] 從自定義外掛開始說起Flutter
- [外掛擴充套件]onethink自定義欄位外掛 百度地圖定位 外掛套件地圖
- 【Maven實戰技巧】「外掛使用專題」Maven-Archetype外掛建立自定義maven專案骨架Maven
- svelte元件:Svelte3自定義Navbar+Tabbr元件|svelte自定義外掛元件
- iOS持續整合(三)——fastlane 自定義外掛iOSAST
- Higress 基於自定義外掛訪問 RedisRedis
- Java整合系列:高效構建自定義外掛Java
- JMeter自定義取樣器外掛開發JMeter
- [外掛擴充套件]自定義表單外掛【2015-02-28更新】套件
- Headshot外掛如何使用?Headshot外掛使用教程
- ##自定義一個自動注入Log 的 外掛
- Qt自定義外掛plugin的開發和呼叫QTPlugin
- IDEA Web渲染外掛開發(二)— 自定義JsDialogIdeaWebJS
- Custom Post Types [2.1.14] - WordPress高階自定義外掛