1.selenium 截圖的四種方式
1. save_sreenshoot # 一般不用 坑多 2. get_sreenshoot_as_file # 保存網頁截圖 3. get_sreenshoot_as_png #獲取二進制數據流 4.get_sreenshoot_as_base64 # base64編碼原始數據
2.allure的鈎子函數
Hooks 函數獲取用例執行結果(pytest_runtest_makereport)
看一下ruuner的源碼 pytest執行測試原理 setup call teardown
_pytest.runner
from _pytest import runner # 對應源碼 def pytest_runtest_makereport(item, call): """ return a :py:class:`_pytest.runner.TestReport` object for the given :py:class:`pytest.Item` and :py:class:`_pytest.runner.CallInfo`. """
這里item是測試用例,call是測試步驟,具體執行過程如下:
- 先執行when='setup' 返回setup 的執行結果
- 然后執行when='call' 返回call 的執行結果
- 最后執行when='teardown'返回teardown 的執行結果
運行實例
conftest.py 寫 pytest_runtest_makereport 內容,打印運行過程和運行結果
# conftest.py import pytest @pytest.hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_makereport(item, call): print('------------------------------------') # 獲取鈎子方法的調用結果 out = yield print('用例執行結果', out) # 3. 從鈎子方法的調用結果中獲取測試報告 report = out.get_result() print('測試報告:%s' % report) print('步驟:%s' % report.when) print('nodeid:%s' % report.nodeid) print('description:%s' % str(item.function.__doc__)) print(('運行結果: %s' % report.outcome))
test_a.py寫一個簡單的用例
def test_a(): '''用例描述:test_a''' print("上海-悠悠")
運行結果如下
D:\soft\code\pytest_jenkins_demo\demo>pytest -s ============================= test session starts ============================= platform win32 -- Python 3.6.0, pytest-4.5.0, py-1.5.4, pluggy-0.13.1 rootdir: D:\demo plugins: html-1.19.0, collected 1 item test_a.py ------------------------------------ 用例執行結果 <pluggy.callers._Result object at 0x0000027C547332B0> 測試報告:<TestReport 'test_a.py::test_a' when='setup' outcome='passed'> 步驟:setup nodeid:test_a.py::test_a description:用例描述:test_a 運行結果: passed 上海-悠悠 ------------------------------------ 用例執行結果 <pluggy.callers._Result object at 0x0000027C547332B0> 測試報告:<TestReport 'test_a.py::test_a' when='call' outcome='passed'> 步驟:call nodeid:test_a.py::test_a description:用例描述:test_a 運行結果: passed .------------------------------------ 用例執行結果 <pluggy.callers._Result object at 0x0000027C54750A20> 測試報告:<TestReport 'test_a.py::test_a' when='teardown' outcome='passed'> 步驟:teardown nodeid:test_a.py::test_a description:用例描述:test_a 運行結果: passed ========================== 1 passed in 0.06 seconds ===========================
從運行結果可以看出,運行用例的過程會經歷三個階段:setup-call-teardown,每個階段都會返回的 Result 對象和 TestReport 對象,以及對象屬性。
setup和teardown上面的用例默認都沒有,結果都是passed
setup和teardown
給用例寫個fixture增加用例的前置和后置操作,conftest.py內容如下
import pytest @pytest.hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_makereport(item, call): print('------------------------------------') # 獲取鈎子方法的調用結果 out = yield print('用例執行結果', out) # 3. 從鈎子方法的調用結果中獲取測試報告 report = out.get_result() print('測試報告:%s' % report) print('步驟:%s' % report.when) print('nodeid:%s' % report.nodeid) print('description:%s' % str(item.function.__doc__)) print(('運行結果: %s' % report.outcome)) @pytest.fixture(scope="session", autouse=True) def fix_a(): print("setup 前置操作") yield print("teardown 后置操作")
運行結果如下
setup失敗情況
當setup執行失敗了,setup的執行結果的failed,后面的call用例和teardown都不會執行了
此時用例的狀態是:error, 也就是用例(call)都還沒開始執行,就異常了
call失敗情況
如果setup正常執行,但是測試用例call失敗了
@pytest.fixture(scope="session", autouse=True) def fix_a(): print("setup 前置操作") yield print("teardown 后置操作")
test_a.py用例
def test_a(): '''用例描述:test_a''' print("上海-悠悠") assert 1==0
那么此時運行的結果就是failed
teardown失敗了
如果setup正常執行,測試用例call正常執行,teardown失敗了,這種情況
@pytest.fixture(scope="session", autouse=True) def fix_a(): print("setup 前置操作") yield print("teardown 后置操作") raise Exception("teardown 失敗了")
teat_a.py用例
def test_a(): '''用例描述:test_a''' print("上海-悠悠")
最終統計的結果: 1 passed, 1 error in 0.16 seconds
只獲取call的結果
我們在寫用例的時候,如果保證setup和teardown不報錯情況,只關注測試用例本身的運行結果,前面的 pytest_runtest_makereport 鈎子方法執行了三次。
可以加個判斷:if report.when == "call"
import pytest from _pytest import runner ''' # 對應源碼 def pytest_runtest_makereport(item, call): """ return a :py:class:`_pytest.runner.TestReport` object for the given :py:class:`pytest.Item` and :py:class:`_pytest.runner.CallInfo`. """ ''' @pytest.hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_makereport(item, call): print('------------------------------------') # 獲取鈎子方法的調用結果 out = yield # print('用例執行結果', out) # 3. 從鈎子方法的調用結果中獲取測試報告 report = out.get_result() if report.when == "call": print('測試報告:%s' % report) print('步驟:%s' % report.when) print('nodeid:%s' % report.nodeid) print('description:%s' % str(item.function.__doc__)) print(('運行結果: %s' % report.outcome)) @pytest.fixture(scope="session", autouse=True) def fix_a(): print("setup 前置操作") yield print("teardown 后置操作")
運行結果
3.allure報告集成錯誤截圖
需要使用conftest.py文件,conftest.py需要存在在測試目錄中,文件名不能變更,可以根據模塊創建層級嵌套。
具體參照pytest的官方文檔
@pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): ''' hook pytest失敗 :param item: :param call: :return: ''' # execute all other hooks to obtain the report object outcome = yield rep = outcome.get_result() # we only look at actual failing test calls, not setup/teardown if rep.when == "call" and rep.failed: mode = "a" if os.path.exists("failures") else "w" with open("failures", mode) as f: # let's also access a fixture for the fun of it if "tmpdir" in item.fixturenames: extra = " (%s)" % item.funcargs["tmpdir"] else: extra = "" f.write(rep.nodeid + extra + "\n") # pic_info = adb_screen_shot() with allure.step('添加失敗截圖...'): allure.attach(driver.get_screenshot_as_png(), "失敗截圖", allure.attachment_type.PNG)
4.Pytest+Allure+Selenuim+異常截屏+Allure日志
需要使用conftest.py文件,conftest.py需要存在在測試目錄中,文件名不能變更,可以根據模塊創建層級嵌套。具體參照pytest的官方文檔