pytest測試框架進階-conftest文件重寫采集和運行測試用例的hook函數


使用pytest不僅僅局限於進行單元測試,作為底層模塊可擴展性強,有必要理解其運行機制,便於進行二次開發擴展,通過文檔的學習很容易理解。

構建一個簡單的測試腳本

import pytest
import requests

def add(a,b):
if type(a) is str or type(b) is str:
return str(a) + str(b)
return a+b

def chengfa(a,b):
if type(a) is str or type(b) is str:
return 0
return a*b

class TestMath(object):

@pytest.fixture(scope='session',autouse=True)
def starter(self):
print('開始')
yield
print('結束')

def testadd(self):
'''測試加法程序'''
print("正在執行testadd")
assert add('a',1) == 'a1'
print("驗證成功")

def testchengfa(self):
'''測試加法程序'''
print("正在執行testadd")
assert chengfa('a',1) == 'a'
print("驗證成功")

if __name__ == '__main__':
pytest.main(['-s','testmath.py'])

 

采集測試用例相關函數

@pytest.hookimpl(hookwrapper=True)
def pytest_collection(session):
print("當前運行"+sys._getframe().f_code.co_name)
print('啟動測試采集器'+str(session))
result = yield
print('最終測試采集結果' + str(session.items))
print("結束運行" + sys._getframe().f_code.co_name)
print("\n")


@pytest.hookimpl(hookwrapper=True)
def pytest_collectstart(collector):
print("當前運行" + sys._getframe().f_code.co_name)
print("當前節點" +collector.nodeid)
result = yield
print("結束運行" + sys._getframe().f_code.co_name)
print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_make_collect_report(collector):
print("當前運行" + sys._getframe().f_code.co_name)
result = yield
print("當前節點" +result.get_result().nodeid + ",采集結果:"+result.get_result().outcome+ ",采集節點為:"+str(result.get_result().result))
print("結束運行" + sys._getframe().f_code.co_name)
print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_pycollect_makemodule(path, parent):
print("當前運行" + sys._getframe().f_code.co_name)
print('在目錄' + str(parent.fspath) + '采集到測試腳本'+str(path))
result = yield
print('當前采集模塊' + result.get_result().nodeid)
print("結束運行" + sys._getframe().f_code.co_name)
print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_generate_tests(metafunc):
print("當前運行" + sys._getframe().f_code.co_name)
result = yield
print("結束運行" + sys._getframe().f_code.co_name)
print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_collectreport(report):
print("當前運行" + sys._getframe().f_code.co_name)
print('在節點' + report.nodeid + '采集到' + str(report.result))
result = yield
print("結束運行" + sys._getframe().f_code.co_name)
print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_collection_modifyitems(session, config, items):
print("當前運行" + sys._getframe().f_code.co_name)
result = yield
print('測試順序為'+ str(items))
print("結束運行" + sys._getframe().f_code.co_name)
print("\n")



@pytest.hookimpl(hookwrapper=True)
def pytest_collection_finish(session):
print("當前運行" + sys._getframe().f_code.co_name)
result = yield
print('用例采集完成')
print("結束運行" + sys._getframe().f_code.co_name)
print("\n")

 

pytest_collection(session)

執行給定會話的采集協議。循環運行pytest_collectstart,pytest_make_collect_report遍歷查找測試用例,直到所有用例采集成功

session:Session對象,基類_pytest.nodes.FSCollector

 

pytest_collectstart(collector)

Collector開始采集。

collector:Collector對象

采集器實例通過collect()創建子項,從而迭代地構建樹。意思就是來尋找符合規則的測試節點變成Collector的nodeid,給pytest_make_collect_report使用。

 

pytest_make_collect_report(collector)

執行collector.collect()並返回CollectReport對象。返回采集當前節點采集測試節點是否成功,如果當前采集到節點是方法,會運行pytest_generate_tests生成測試用例對象。

 

pytest_pycollect_makemodule(path, parent) 

path:pytest測試的根目錄,也可通過命令行設置,例如pytest C://xxx.py,path就為C://xxx.py

parent:任何新節點都需要將指定parent的父節點作為父節點

根據path目錄向下查找,提取存在測試類的py文件。將為每個匹配的測試模塊路徑調用此Hook方法。如果要為不匹配的文件創建測試模塊作為測試模塊,則需要使用pytest_collect_fileHook方法。

 

pytest_collectreport(report)

report:CollectReport對象采集報告

Collector完成采集時調用,pytest_make_collect_report采集結果成功或失敗,失敗則報異常

 

pytest_generate_tests(metafunc)

metafunc: Metafunc對象

生成測試用例的方法,將自定義的fixture、parameters給測試函數調用變成測試用例對象。

 

pytest_collection_modifyitems(session, config, items):

config:Config對象,根據配置進行相應行為

 在執行收集后調用,可以就地過濾或重新排序項目。

 

pytest_collection_finish(session)

返回最終采集結果及數量

 

運行測試用例相關函數

@pytest.hookimpl(hookwrapper=True)
def pytest_runtestloop(session):
print("當前運行" + sys._getframe().f_code.co_name)
print('開始測試測試用例集合' + str(session.items))
result = yield
print('測試用例集合測試結果為' + str(session))
print("結束運行" + sys._getframe().f_code.co_name)
print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_protocol(item,nextitem):
print("當前運行" + sys._getframe().f_code.co_name)
print('開始測試用例:'+str(item.name))
result = yield
print("結束運行" + sys._getframe().f_code.co_name)
print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_setup(item):
print("當前運行" + sys._getframe().f_code.co_name)
print('執行setup模塊')
result = yield
print("結束運行" + sys._getframe().f_code.co_name)
print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_call(item):
print("當前運行" + sys._getframe().f_code.co_name)
result = yield
print("結束運行" + sys._getframe().f_code.co_name)
print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_teardown(item):
print("當前運行" + sys._getframe().f_code.co_name)
print('執行teardown模塊')
result = yield
print("結束運行" + sys._getframe().f_code.co_name)
print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_fixture_post_finalizer(fixturedef,request):
print("當前運行" + sys._getframe().f_code.co_name)
print('開始卸載fixture模塊-' + str(request.fixturename))
result = yield
print("結束運行" + sys._getframe().f_code.co_name)
print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_fixture_setup(fixturedef,request):
print("當前運行" + sys._getframe().f_code.co_name)
# print('開始執行fixture模塊-' + str(request.fixturename))
result = yield
# if result.excinfo == None:
# print(request.fixturename + '運行完畢')
# else:
# print('出現異常' + str(result.excinfo))
print("結束運行" + sys._getframe().f_code.co_name)
print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item,call):
print("當前運行" + sys._getframe().f_code.co_name)
print(str(item.name) + str(call.when) + '運行結束')
result = yield
print(result.get_result().when + "階段測試結果:" + result.get_result().outcome)
print("結束運行" + sys._getframe().f_code.co_name)
print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_pyfunc_call(pyfuncitem):
print("當前運行" + sys._getframe().f_code.co_name)
print('執行test_模塊' + str(pyfuncitem))
result = yield
print("結束運行" + sys._getframe().f_code.co_name)
print("\n")

 

運行結果

 

pytest_runtestloop(session)

收集完成后執行所有采集到的測試用例,調用pytest_runtest_protocol循環調用測試用例對象。

 

pytest_runtest_protocol(item,nextitem)

item:當前測試用例對象

nextitem:下一個測試用例

依次調用pytest_runtest_setup,pytest_runtest_call,pytest_runtest_teardown進行循環測試,本次測試用例運行不出現程序異常就返回true,非錯誤

 

pytest_runtest_setup(item)

調用以執行采集的測試項的setup階段。運行當前的測試用例測試前需要調用pytest_fixture_setup方法運行fixture函數

 

pytest_runtest_call(item)

調用以執行采集的測試項。

 

pytest_runtest_teardown

調用以執行采集的測試項的setup階段。銷毀當前的測試用例測試前運行的fixture函數

 

pytest_fixture_setup(fixturedef,request)

查找並執行所有的fixture函數。

 

pytest_fixture_post_finalizer(fixturedef,request)

測試用例運行結束后銷毀fixture

 

pytest_pyfunc_call(pyfuncitem: Function)

運行測試方法pyfuncitem

 

生成測試報告相關函數

@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_logreport(report):
    print("當前運行" + sys._getframe().f_code.co_name)
    result = yield
    print("結束運行" + sys._getframe().f_code.co_name)
    print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_report_header(config, startdir):
    print("當前運行" + sys._getframe().f_code.co_name)
    result = yield
    print("結束運行" + sys._getframe().f_code.co_name)
    print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_report_collectionfinish(config, startdir, items) :
    print("當前運行" + sys._getframe().f_code.co_name)
    result = yield
    print("結束運行" + sys._getframe().f_code.co_name)
    print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_report_teststatus(report, config):
    print("當前運行" + sys._getframe().f_code.co_name)
    result = yield
    print(result.get_result())
    print("結束運行" + sys._getframe().f_code.co_name)
    print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_assertrepr_compare(config,op,left,right):
    print("當前運行" + sys._getframe().f_code.co_name)
    print('開始斷言' + str(left) + str(op) + str(right))
    result = yield
    print('斷言結果為:' + str(result.get_result()))
    print("結束運行" + sys._getframe().f_code.co_name)
    print("\n")

@pytest.hookimpl(hookwrapper=True)
def pytest_exception_interact(call, report):
    print("當前運行" + sys._getframe().f_code.co_name)
    print(str(call.excinfo))
    # print(str(report.longreprtext))
    result = yield
    print("結束運行" + sys._getframe().f_code.co_name)
    print("\n")


@pytest.hookimpl(hookwrapper=True)
def pytest_terminal_summary(terminalreporter,exitstatus,config):
    print("當前運行" + sys._getframe().f_code.co_name)
    print('此次測試結果為' + str(exitstatus))
    print('通過的用例為' + str(terminalreporter.stats['passed']))
    print('失敗的用例為' + str(terminalreporter.stats['failed']))
    result = yield
    print("結束運行" + sys._getframe().f_code.co_name)
    print("\n")

 

pytest_runtest_makereport(item,call)

call:CallInfo對象,可以通過參數查看測試結果/異常信息,具體參數參考CallInfo

當pytest_runtest_setup,pytest_runtest_call,pytest_runtest_teardown運行完,生成一個TestReport對象。

TestReport對象:基本測試報告對象

 

pytest_report_teststatus(report, config)

根據pytest_runtest_makereport運行返回測試結果的集合成功為('passed', '.', 'PASSED'),失敗為('failed', 'F', 'FAILED')。

 

pytest_assertrepr_compare(config,op,left,right)

op:比較符號

用例assert時調用,返回失敗的斷言表達式中的比較解釋。

 

pytest_runtest_logreport(report)

根據report打印測試用例運行結果

 

pytest_exception_interact(call, report)

pytest_report_teststatus結果運行失敗,在引發異常時調用,可以交互式處理。只有在引發的異常不是內部異常, 如skip.Exception時才會調用此Hook方法。

 

pytest_terminal_summary(terminalreporter,exitstatus,config)

所有用例對象遍歷完成后,對結果進行統計報告。

 

pytest_report_header(config: Config, startdir: py._path.local.LocalPath)

返回要顯示為標題信息的字符串或字符串列表,以進行終端報告。

 

pytest_report_collectionfinish(config: Config, startdir: py._path.local.LocalPath, items: Sequence[Item])

返回成功完成收集后將顯示的字符串或字符串列表。

 

輸出結果:

當前運行pytest_report_header
結束運行pytest_report_header

 
         


rootdir: D:\PycharmProjects\seleniumtest
plugins: allure-pytest-2.8.22, html-3.0.0, metadata-1.10.0
當前運行pytest_collection
啟動測試采集器<Session seleniumtest exitstatus=<ExitCode.OK: 0> testsfailed=0 testscollected=0>
當前運行pytest_collectstart
當前節點
結束運行pytest_collectstart

 
         


當前運行pytest_make_collect_report
當前運行pytest_pycollect_makemodule
在目錄D:\PycharmProjects\seleniumtest采集到測試腳本D:\PycharmProjects\seleniumtest\testmath.py
當前采集模塊testmath.py
結束運行pytest_pycollect_makemodule

 
         


當前節點,采集結果:passed,采集節點為:[<Module testmath.py>]
結束運行pytest_make_collect_report

 
         


當前運行pytest_collectreport
在節點采集到[<Module testmath.py>]
結束運行pytest_collectreport

 
         


當前運行pytest_collectstart
當前節點testmath.py
結束運行pytest_collectstart

 
         


當前運行pytest_make_collect_report
當前節點testmath.py,采集結果:passed,采集節點為:[<Class TestMath>]
結束運行pytest_make_collect_report

 
         


當前運行pytest_collectstart
當前節點testmath.py::TestMath
結束運行pytest_collectstart

 
         


當前運行pytest_make_collect_report
當前節點testmath.py::TestMath,采集結果:passed,采集節點為:[<Instance ()>]
結束運行pytest_make_collect_report

 
         


當前運行pytest_collectstart
當前節點testmath.py::TestMath
結束運行pytest_collectstart

 
         


當前運行pytest_make_collect_report
當前運行pytest_generate_tests
結束運行pytest_generate_tests

 
         


當前運行pytest_generate_tests
結束運行pytest_generate_tests

 
         


當前節點testmath.py::TestMath,采集結果:passed,采集節點為:[<Function testadd>, <Function testchengfa>]
結束運行pytest_make_collect_report

 
         


當前運行pytest_collectreport
在節點testmath.py::TestMath采集到[<Function testadd>, <Function testchengfa>]
結束運行pytest_collectreport

 
         


當前運行pytest_collectreport
在節點testmath.py::TestMath采集到[<Instance ()>]
結束運行pytest_collectreport

 
         


當前運行pytest_collectreport
在節點testmath.py采集到[<Class TestMath>]
結束運行pytest_collectreport

 
         


當前運行pytest_collection_modifyitems
測試順序為[<Function testadd>, <Function testchengfa>]
結束運行pytest_collection_modifyitems

 
         


當前運行pytest_collection_finish
collected 2 items
當前運行pytest_report_collectionfinish
結束運行pytest_report_collectionfinish

 
         


用例采集完成
結束運行pytest_collection_finish

 
         


最終測試采集結果[<Function testadd>, <Function testchengfa>]
結束運行pytest_collection

 
         


當前運行pytest_runtestloop
開始測試測試用例集合[<Function testadd>, <Function testchengfa>]
當前運行pytest_runtest_protocol
開始測試用例:testadd

 
         

testmath.py 當前運行pytest_runtest_setup
執行setup模塊
當前運行pytest_fixture_setup
開始執行fixture模塊-starter
開始
starter運行完畢
結束運行pytest_fixture_setup

 
         


結束運行pytest_runtest_setup

 
         


當前運行pytest_runtest_makereport
testaddsetup運行結束
setup階段測試結果:passed
結束運行pytest_runtest_makereport

 
         


當前運行pytest_runtest_logreport
當前運行pytest_report_teststatus
('', '', '')
結束運行pytest_report_teststatus

 
         


結束運行pytest_runtest_logreport

 
         


當前運行pytest_runtest_call
當前運行pytest_pyfunc_call
執行test_模塊<Function testadd>
正在執行testadd
驗證成功
結束運行pytest_pyfunc_call

 
         


結束運行pytest_runtest_call

 
         


當前運行pytest_runtest_makereport
testaddcall運行結束
call階段測試結果:passed
結束運行pytest_runtest_makereport

 
         


當前運行pytest_runtest_logreport
當前運行pytest_report_teststatus
('passed', '.', 'PASSED')
結束運行pytest_report_teststatus

 
         


.結束運行pytest_runtest_logreport

 
         


當前運行pytest_runtest_teardown
執行teardown模塊
結束運行pytest_runtest_teardown

 
         


當前運行pytest_runtest_makereport
testaddteardown運行結束
teardown階段測試結果:passed
結束運行pytest_runtest_makereport

 
         


當前運行pytest_runtest_logreport
當前運行pytest_report_teststatus
('', '', '')
結束運行pytest_report_teststatus

 
         


結束運行pytest_runtest_logreport

 
         


結束運行pytest_runtest_protocol

 
         


當前運行pytest_runtest_protocol
開始測試用例:testchengfa
當前運行pytest_runtest_setup
執行setup模塊
結束運行pytest_runtest_setup

 
         


當前運行pytest_runtest_makereport
testchengfasetup運行結束
setup階段測試結果:passed
結束運行pytest_runtest_makereport

 
         


當前運行pytest_runtest_logreport
當前運行pytest_report_teststatus
('', '', '')
結束運行pytest_report_teststatus

 
         


結束運行pytest_runtest_logreport

 
         


當前運行pytest_runtest_call
當前運行pytest_pyfunc_call
執行test_模塊<Function testchengfa>
正在執行testadd
當前運行pytest_assertrepr_compare
開始斷言0==a
斷言結果為:[]
結束運行pytest_assertrepr_compare

 
         


結束運行pytest_pyfunc_call

 
         


結束運行pytest_runtest_call

 
         


當前運行pytest_runtest_makereport
testchengfacall運行結束
call階段測試結果:failed
結束運行pytest_runtest_makereport

 
         


當前運行pytest_runtest_logreport
當前運行pytest_report_teststatus
('failed', 'F', 'FAILED')
結束運行pytest_report_teststatus

 
         


F結束運行pytest_runtest_logreport

 
         


當前運行pytest_exception_interact
<ExceptionInfo AssertionError("assert 0 == 'a'\n + where 0 = chengfa('a', 1)") tblen=1>
結束運行pytest_exception_interact

 
         


當前運行pytest_runtest_teardown
執行teardown模塊
結束
當前運行pytest_fixture_post_finalizer
開始卸載fixture模塊-starter
結束運行pytest_fixture_post_finalizer

 
         


當前運行pytest_fixture_post_finalizer
開始卸載fixture模塊-starter
結束運行pytest_fixture_post_finalizer

 
         


結束運行pytest_runtest_teardown

 
         


當前運行pytest_runtest_makereport
testchengfateardown運行結束
teardown階段測試結果:passed
結束運行pytest_runtest_makereport

 
         


當前運行pytest_runtest_logreport
當前運行pytest_report_teststatus
('', '', '')
結束運行pytest_report_teststatus

 
         


結束運行pytest_runtest_logreport

 
         


結束運行pytest_runtest_protocol

 
         


測試用例集合測試結果為<Session seleniumtest exitstatus=<ExitCode.OK: 0> testsfailed=1 testscollected=2>
結束運行pytest_runtestloop

 
         

 

 
         


================================== FAILURES ===================================
____________________________ TestMath.testchengfa _____________________________

 
         

self = <testmath.TestMath object at 0x039F6BD0>

 
         

def testchengfa(self):
'''測試加法程序'''
print("正在執行testadd")
> assert chengfa('a',1) == 'a'
E AssertionError: assert 0 == 'a'
E + where 0 = chengfa('a', 1)

 
         

testmath.py:31: AssertionError
當前運行pytest_terminal_summary
此次測試結果為ExitCode.TESTS_FAILED
通過的用例為[<TestReport 'testmath.py::TestMath::testadd' when='call' outcome='passed'>]
失敗的用例為[<TestReport 'testmath.py::TestMath::testchengfa' when='call' outcome='failed'>]
結束運行pytest_terminal_summary

 
         


當前運行pytest_report_teststatus
('failed', 'F', 'FAILED')
結束運行pytest_report_teststatus

 
         


=========================== short test summary info ===========================
FAILED testmath.py::TestMath::testchengfa - AssertionError: assert 0 == 'a'
========================= 1 failed, 1 passed in 0.12s =========================

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM