書籍來源:房荔枝 梁麗麗《pytest架構與自動化測試應用》
一邊學習一邊整理老師的課程内容及實驗筆記,并與大家分享,侵權即删,謝謝支援!
附上彙總貼:pytest架構進階自學系列 | 彙總_熱愛程式設計的通信人的部落格-CSDN部落格
失敗時加載PDB環境
在工具欄中會有一個小蟲子的圖示,一般筆者在調試時使用這個檢視變量等的情況。PDB(Python Debugger)是Python内建的調試器,如果習慣使用它也是一個不錯的選擇。pytest允許通過以下指令在執行失敗時進入這個調試器模式,代碼如下:
舉例說明:
pytest會在測試用例失敗(或者Ctrl+C)時,調用這個調試器,可以通路測試用例的本地變量x。失敗的資訊存儲在sys.last_value、sys.last_type、sys.last_traceback變量中,可以在互動環境中通路它們。使用exit指令,即可退出PDB環境。
代碼如下:
def test_fail():
x = 1
assert x == 0
執行pytest --pdb test_pdb.py,結果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2> pytest --pdb .\test_pdb.py
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 1 item
test_pdb.py F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
def test_fail():
x = 1
> assert x == 0
E assert 1 == 0
test_pdb.py:3: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> PDB post_mortem (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> d:\synologydrive\codelearning\win\pytest-book\src\chapter-2\test_pdb.py(3)test_fail()
-> assert x == 0
(Pdb) import sys
(Pdb) sys.last_value
AssertionError('assert 1 == 0')
(Pdb) sys.last_type
<class 'AssertionError'>
(Pdb) sys.last_traceback
<traceback object at 0x00000157D9A21688>
(Pdb) exit
======================================================================================================= short test summary info =======================================================================================================
FAILED test_pdb.py::test_fail - assert 1 == 0
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! _pytest.outcomes.Exit: Quitting debugger !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
========================================================================================================= 1 failed in 56.14s ==========================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2>
開始執行時就加載PDB環境
通過以下指令,pytest允許在每個測試用例開始執行時就加載PDB環境:
設定斷點
通常可以通過在代碼行号附近單擊具體行設定一個斷點,在單擊後運作會停下來。如圖所示。
也可以在測試用例代碼中添加import pdb;pdb.set_trace(),當其被調用時,pytest會停止這條用例的輸出,其他用例不受影響。通過continue指令,退出PDB環境,并繼續執行用例。
代碼如下:
def test_fail():
x = 1
import pdb
pdb.set_trace()
assert x == 0
執行結果如下,在進行(Pdb)後,輸入x,傳回1。輸入import sys,無傳回。輸入sys.path,傳回目前系統路徑。輸入continue,用例接着運作,輸出結果如下。
D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2\test_pdb.py
Testing started at 21:07 ...
Launching pytest with arguments D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2\test_pdb.py in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2
============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2, inifile: pytest.ini
plugins: allure-pytest-2.13.2, collect-formatter2-0.1.3
collecting ... collected 1 item
test_pdb.py::test_fail
>>>>>>>>>>>>>>>>>>> PDB set_trace (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>
> d:\synologydrive\codelearning\win\pytest-book\src\chapter-2\test_pdb.py(5)test_fail()
-> assert x == 0
(Pdb) 1
(Pdb) (Pdb) ['D:\\SynologyDrive\\CodeLearning\\WIN\\pytest-book\\src\\chapter-2', 'C:\\Program Files\\JetBrains\\PyCharm Community Edition 2022.3.2\\plugins\\python-ce\\helpers\\pycharm', 'D:\\SynologyDrive\\CodeLearning\\WIN\\pytest-book', 'C:\\Users\\guoliang\\AppData\\Local\\Programs\\Python\\Python37\\python37.zip', 'C:\\Users\\guoliang\\AppData\\Local\\Programs\\Python\\Python37\\DLLs', 'C:\\Users\\guoliang\\AppData\\Local\\Programs\\Python\\Python37\\lib', 'C:\\Users\\guoliang\\AppData\\Local\\Programs\\Python\\Python37', 'D:\\SynologyDrive\\CodeLearning\\WIN\\pytest-book\\venv', 'D:\\SynologyDrive\\CodeLearning\\WIN\\pytest-book\\venv\\lib\\site-packages']
(Pdb)
>>>>>>>>>>>>>>>>>>>>> PDB continue (IO-capturing resumed) >>>>>>>>>>>>>>>>>>>>>
FAILED [100%]
test_pdb.py:0 (test_fail)
1 != 0
Expected :0
Actual :1
<Click to see difference>
def test_fail():
x = 1
import pdb
pdb.set_trace()
> assert x == 0
E assert 1 == 0
E +1
E -0
test_pdb.py:5: AssertionError
================================== FAILURES ===================================
__________________________________ test_fail __________________________________
def test_fail():
x = 1
import pdb
pdb.set_trace()
> assert x == 0
E assert 1 == 0
E +1
E -0
test_pdb.py:5: AssertionError
=========================== short test summary info ===========================
FAILED test_pdb.py::test_fail - assert 1 == 0
============================= 1 failed in 31.71s ==============================
Process finished with exit code 1
使用内置的中斷函數
Python 3.7中有一個内置breakpoint()函數。pytest可以在以下場景中使用:
當breakpoint()被調用,并且PYTHONBREAKPOINT為None時,pytest會使用内部自定義的PDB代替系統的PDB,測試執行結束時,自動切換回系統自帶的PDB。
當加上--pdb選項時,breakpoint()和測試發生錯誤時,都會調用内部自定義的PDB,--pdbcls選項允許指定一個使用者自定義的PDB類。
錯誤句柄
這是pytest 5.0版本新增特性,在測試中發生段錯誤或者逾時的情況下,faulthandler标準子產品可以轉存Python的回溯資訊,它在pytest的執行中預設為已加載,使用-p no:faulthandler選項可以關閉它。同樣,faulthandler_timeout=X配置項可用于當測試用例的完成時間超過X秒時,轉存所有線程的Python回溯資訊。
舉例說明,在配置檔案中設定測試執行的逾時時間為5s,代碼如下:
[pytest]
faulthandler_timeout=5
在測試用例中添加等待7s的操作,代碼如下:
import time
def test_faulthandler():
time.sleep(7)
assert 1
當預設已加載faulthandler時,輸入pytest-q test_fault_handler.py,執行結果如下,顯示Timeout(0:00:05)。在執行剛超過5s的時候會列印出回溯資訊,但不會中斷測試的執行。
執行結果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2> pytest -q .\test_fault_handler.py
Timeout (0:00:05)!
Thread 0x00005e58 (most recent call first):
File "D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2\test_fault_handler.py", line 4 in test_faulthandler
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\python.py", line 184 in pytest_pyfunc_call
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\callers.py", line 187 in _multicall
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\manager.py", line 87 in <lambda>
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\manager.py", line 93 in _hookexec
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\hooks.py", line 286 in __call__
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\python.py", line 1479 in runtest
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\runner.py", line 135 in pytest_runtest_call
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\callers.py", line 187 in _multicall
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\manager.py", line 87 in <lambda>
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\manager.py", line 93 in _hookexec
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\hooks.py", line 286 in __call__
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\runner.py", line 217 in <lambda>
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\runner.py", line 244 in from_call
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\runner.py", line 217 in call_runtest_hook
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\runner.py", line 186 in call_and_report
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\runner.py", line 100 in runtestprotocol
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\runner.py", line 85 in pytest_runtest_protocol
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\callers.py", line 187 in _multicall
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\manager.py", line 87 in <lambda>
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\manager.py", line 93 in _hookexec
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\hooks.py", line 286 in __call__
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\main.py", line 272 in pytest_runtestloop
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\callers.py", line 187 in _multicall
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\manager.py", line 87 in <lambda>
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\manager.py", line 93 in _hookexec
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\hooks.py", line 286 in __call__
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\main.py", line 247 in _main
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\main.py", line 191 in wrap_session
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\main.py", line 240 in pytest_cmdline_main
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\callers.py", line 187 in _multicall
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\manager.py", line 87 in <lambda>
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\manager.py", line 93 in _hookexec
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\pluggy\hooks.py", line 286 in __call__
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\config\__init__.py", line 125 in main
File "C:\Users\guoliang\AppData\Local\Programs\Python\Python37\Scripts\pytest.exe\__main__.py", line 9 in <module>
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\runpy.py", line 85 in _run_code
File "c:\users\guoliang\appdata\local\programs\python\python37\lib\runpy.py", line 193 in _run_module_as_main
. [100%]
1 passed in 7.09s
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2>
接下來看一下去掉faulthandler的情況,逾時并不會觸發回溯資訊的列印,輸入pytest -q -p no:faulthandler test_fault_handler.py之後的結果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2> pytest -q -p no:faulthandler test_fault_handler.py
. [100%]
1 passed in 7.02s
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-2>
注意:這個功能是從pytest -faulthandler插件合并而來的,但是有兩點不同:去掉此功能時,使用-p no:faulthandler代替原來的--no-faulthandler;使用faulthandler_timeout配置項代替--faulthandler-timeout指令行選項來配置逾時時間。當然,也可以使用-o faulthandler_timeout=X在指令行進行配置。