pytest_runtest_makereport调用顺序

pytest提供了pytest_runtest_makereport这个方法,可以捕获用例的执行情况。根据官方提供的示例,在conftest.py文件中添加如下代码就可以捕获每个用例的执行结果。(官方示例链接:
https://docs.pytest.org/en/7.1.x/example/simple.html?highlight=pytest_runtest_makereport)

@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    # execute all other hooks to obtain the report object
    outcome = yield
    rep = outcome.get_result()  # rep可以拿到用例的执行结果详情

个人有两种理解:

1.这个钩子方法会被每个测试用例调用 3 次,分别是:

  1. 用例步骤 setup 执行完毕后,调用 1 次,返回 setup 的执行结果并执行钩子函数中setup相关代码-->接着执行用例测试步骤;
  2. 用例步骤 call 执行完毕之后,调用 1 次,返回测试用例的执行结果并执行钩子函数中call相关的代码-->接着执行teardown相关步骤(若有);
  3. 用例步骤 teardown 执行完毕后,调用1 次,返回 teardown 的执行结果

2.钩子函数和用例交叉执行:

1.测试用例setup步骤-->钩子函数(pytest_runtest_makereport)获取setup结果并执行setup相符的相关代码-->测试用例执行call 步骤-->钩子函数获取call 结果并执行call相符的相关代码-->测试用例执行teardown 步骤-->钩子函数获取teardown 结果并执行teardown相符的相关代码

调用钩子函数

# -*- coding: utf-8 -*-
# conftest.py


import threading
import time

import pytest
# from temp import th_temp


def th_temp():
    for i in range(10):
        print(i, time.strftime("%Y%m%d%H%M%S"))
        time.sleep(1)


@pytest.hookimpl(trylast=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    outcome = yield
    report = outcome.get_result()
    if report.when == "setup":
        print("\n 开始执行钩子函数setup相关脚本 {}".format(time.strftime("%Y%m%d%H%M%S")))
        t1 = threading.Thread(target=th_temp, )
        # t1.daemon = True
        t1.start()
        time.sleep(10)

    if report.when == "call" and report.failed:
        print("\n 获取call结果并执行符合结果的脚本{}".format(time.strftime("%Y%m%d%H%M%S")))

    if report.when == "teardown" and report.passed:
        print("\n 获取teardown结果并执行相关脚本{}".format(time.strftime("%Y%m%d%H%M%S")))

    print('{} 从结果中获取测试报告:'.format(time.strftime("%Y%m%d%H%M%S")), report)
    print('{} 从报告中获取 nodeid:'.format(time.strftime("%Y%m%d%H%M%S")), report.nodeid)
    print('{} 从报告中获取调用步骤:'.format(time.strftime("%Y%m%d%H%M%S")), report.when)
    print('{} 从报告中获取执行结果:'.format(time.strftime("%Y%m%d%H%M%S")),report.outcome)





测试用例py文件

#test_prac

# -*- coding: utf-8 -*-

import time


def setup_function():
    print("{} setup开始执行".format(time.strftime("%Y%m%d%H%M%S")))
    for i in range(5):
        print(i, time.strftime("%Y%m%d%H%M%S"))
        time.sleep(1)
    print("{} setup执行完毕".format(time.strftime("%Y%m%d%H%M%S")))


def teardown_function():
    print("{} teardown开始执行".format(time.strftime("%Y%m%d%H%M%S")))
    for i in range(5):
        print(i, time.strftime("%Y%m%d%H%M%S"))
        time.sleep(1)
    print("{} teardown执行完毕".format(time.strftime("%Y%m%d%H%M%S")))


def test_01():
    print(1111)

    # assert 1==1


def test_02():
    print("\n 执行测试用例步骤脚本")
    for i in range(5):
        print(i, time.strftime("%Y%m%d%H%M%S"))
        time.sleep(1)
    assert 1 == "2" + time.strftime("%Y%m%d%H%M%S")


def test_03():
    print(33333)

    assert 3 == 3

上述执行结果:

# test1

 开始执行钩子函数setup相关脚本 20240804230252
0 20240804230252
1 20240804230253
2 20240804230254
3 20240804230255
4 20240804230256
5 20240804230257
6 20240804230258
7 20240804230259
8 20240804230300
9 20240804230301
20240804230302 从结果中获取测试报告: <TestReport 'test_prac.py::test_01' when='setup' outcome='passed'>
20240804230302 从报告中获取 nodeid: test_prac.py::test_01
20240804230302 从报告中获取调用步骤: setup
20240804230302 从报告中获取执行结果: passed
20240804230247 setup开始执行
0 20240804230247
1 20240804230248
2 20240804230249
3 20240804230250
4 20240804230251
20240804230252 setup执行完毕
20240804230302 从结果中获取测试报告: <TestReport 'test_prac.py::test_01' when='call' outcome='passed'>
20240804230302 从报告中获取 nodeid: test_prac.py::test_01
20240804230302 从报告中获取调用步骤: call
20240804230302 从报告中获取执行结果: passed
PASSED                                             [ 33%]1111
#test2


 开始执行钩子函数setup相关脚本 20240804230312
0 20240804230312
1 20240804230313
2 20240804230314
3 20240804230315
4 20240804230316
5 20240804230317
6 20240804230319
7 20240804230320
8 20240804230321
9 20240804230322
20240804230322 从结果中获取测试报告: <TestReport 'test_prac.py::test_02' when='setup' outcome='passed'>
20240804230322 从报告中获取 nodeid: test_prac.py::test_02
20240804230322 从报告中获取调用步骤: setup
20240804230322 从报告中获取执行结果: passed
20240804230307 setup开始执行
0 20240804230307
1 20240804230308
2 20240804230309
3 20240804230310
4 20240804230311
20240804230312 setup执行完毕

 获取call结果并执行符合结果的脚本20240804230328
20240804230328 从结果中获取测试报告: <TestReport 'test_prac.py::test_02' when='call' outcome='failed'>
20240804230328 从报告中获取 nodeid: test_prac.py::test_02
20240804230328 从报告中获取调用步骤: call
20240804230328 从报告中获取执行结果: failed
FAILED                                             [ 66%]
 执行测试用例步骤脚本
0 20240804230322
1 20240804230323
2 20240804230324
3 20240804230325
4 20240804230326

test_prac.py:27 (test_02)
1 != '220240804230327'

预期:'220240804230327'
实际:1
<点击以查看差异>

def test_02():
        print("\n 执行测试用例步骤脚本")
        for i in range(5):
            print(i, time.strftime("%Y%m%d%H%M%S"))
            time.sleep(1)
>       assert 1 == "2" + time.strftime("%Y%m%d%H%M%S")
E       AssertionError: assert 1 == ('2' + '20240804230327')
E        +  where '20240804230327' = <built-in function strftime>('%Y%m%d%H%M%S')
E        +    where <built-in function strftime> = time.strftime

test_prac.py:33: AssertionError

具体示例如下:

@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    """
    1.该插件作用于pytest的钩子函数上,可以获取到测试用例不同执行阶段的结果(setup,call,teardown)
    2.可以获取钩子方法的调用结果(返回一个result对象)和调用结果的测试报告(返回一个report对象)
    :param item:
    :param call:
    :return:
    """
    outcome = yield  # 生成器会给outcome send 一个pluggy.callers._Result对象
    rep = outcome.get_result()  # 通过get_result拿到所有钩子函数的结果
    Ntime = time.strftime("%Y%m%d%H%M%S", time.localtime())
    if rep.when == "setup":
        path_info.recording_path = "video/" + Ntime + "_" + \
                                   rep.nodeid.split("::")[1] + ".mp4"
        path_info.recd_thread = threading.Thread(target=Recording, args=(path_info.recording_path,))
        path_info.recd_thread.daemon = True  # 子线程无论是否正在运行都会随着父线程运行完毕后一起退出
        path_info.recd_thread.start() # 执行录屏操作

    if rep.when == "teardown" and rep.passed:
        path_info.stop_recording = 1
        time.sleep(1)
        path_info.stop_recording = 0
        file_oprate.delete_file(path_info.recording_path)

    if rep.when == "call" and rep.failed:
        # 收集失败的用例
        case_info.last_failCase.append(rep.nodeid)

        imag_path = os.path.abspath(".") + r"/" + screen_shot.screen_shot("imags")
        with open(imag_path, "rb") as f:
            img_bit = f.read()
            allure.attach(img_bit, "失败截图", allure.attachment_type.PNG)
            # 当前用例执行的话有很多原因比如点击位置错误展现的弹窗不对,为了不阻塞之后的用例执行,直接进行了杀进程的操作
            pid = processop.check_process_exsit(web_info.web_name)
            if pid != None:
                cmd = 'taskkill /f /im ' + web_info.web_name
                os.system(cmd)

        path_info.stop_recording = 1 #传入参数1则表示停止录屏,不执行对应代码(录屏和截图函数需自己写,本文中的录屏和截图函数已写在其他文件中)
        time.sleep(1)

        with open(path_info.recording_path, "rb") as f:
            video_bit = f.read()
            allure.attach(video_bit, "失败录屏",
                          allure.attachment_type.MP4)  # 可以设置需要显示在allure报告的附件,包含了多种类型,可以通过allure.attachment_type查看支持的类型
        time.sleep(1)
        path_info.stop_recording = 0 #为0则每次本钩子函数执行完后启用录屏操作,录屏相关函数需要已在其他文件中写入,此处不作赘述

参考文章
pytest实现异常用例截图并在allure报告中查看_pytest 实现 allure 报告插入截图-CSDN博客

【pytest】Hook 方法之 pytest_runtest_makereport:获取测试用例执行结果_roport.nodeid-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值