pytest学习-Hooks函数获取用例执行结果

前言:

  pytest提供的很多钩子(Hooks)方法,方便我们对测试用例框架进行二次开发,可以根据自己的需求进行改造。Hook函数又称为钩子函数,它的作用可以理解为勾住自己喜欢的东西,然后对自己喜欢的东西作单独处理。

我写的这段代码包含一个回调函数,当我有喜欢的消息发出时,这个回调函数就会执行,所以说,钩子就是指的回调函数。

先学习一下pytest_runtest_makereport这个钩子方法,可以更清晰的了解用例的执行过程,并获取每个用例的执行结果。

钩子函数有两个作用:

1)可以获取到测试用例不同执行阶段的结果(setup、call、teardown)

2)可以获取钩子方法的调用结果(yield 返回一个result对象)和调用结果的测试报告(返回一个report对象)

pytest的Hook函数,修改pytet-html报告

可以自定义修改pytest-html报告,修改方法如下:

1)在项目根目录添加conftest.py

2)在conftest.py中通过标题行实现自定义钩子函数来修改

pytest_runtest_makereport

  先看一下相关的源代码,在_pytest/runner.py文件中,导入之后点进去查看

 

 

函数执行完成后返回的是测试报告对象,TestReport

 这里的item 是测试用例,call 是测试步骤,具体执行过程如下:

  -先执行when='setup' 返回setup的执行结果

  -然后执行when=‘call’,返回call的执行结果

  -最后执行when='teardown' 返回teardown的执行结果

运行案例:

  conftest.py写pytest_runtest_makereport内容,打印运行过程和运行结果

conftest.py文件如下:

import pytest

@pytest.mark.hookwrapper
def pytest_runtest_makereport(item,call):
    """

    :param item:  测试用例对象
    :param call: 测试用例执行步骤
        执行完常规钩子函数 返回的report报告有个属性叫report.when
        先习性when = 'setup' 返回setup的执行结果
        然后执行when = ‘call' 返回call的执行结果
        然后执行when = 'teardown' 返回 teardown的执行结果
    :return:
    """
    print("------------------------")

    # 获取钩子方法的调用结果
    out = yield
    # 从钩子方法的调用结果中 获取测试报告
    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如下:

import pytest
def test_a():
    print("用例描述 test_a")

  

运行结果:

============================= test session starts =============================
collecting ... collected 1 item

test_a.py::test_a ------------------------
测试报告:<TestReport 'test_a.py::test_a' when='setup' outcome='passed'>
步骤:setup
nodeid:test_a.py::test_a
description : None
运行结果: passed

 fixture login function
------------------------
测试报告:<TestReport 'test_a.py::test_a' when='call' outcome='passed'>
步骤:call
nodeid:test_a.py::test_a
description : None
运行结果: passed
PASSED                                                 [100%]用例描述 test_a
------------------------
测试报告:<TestReport 'test_a.py::test_a' when='teardown' outcome='passed'>
步骤:teardown
nodeid:test_a.py::test_a
description : None
运行结果: passed


============================== 1 passed in 0.01s ==============================

Process finished with exit code 0

  从运行结果可以看出,运行用例的过程将会经历三个阶段:setup-call-teardown,每个阶段后悔返回Result对象和Testreport对象,以及对象属性

setup和teardown 上面的用例默认都没有,结果都是passed

 

setup和teardown:

  给用例写个fixture增加用例的前置和后置操作,conftest.py内容如下:使用pytest -s test_a.py命令执行文件

import pytest

@pytest.mark.hookwrapper
def pytest_runtest_makereport(item,call):
    """

    :param item:  测试用例对象
    :param call: 测试用例执行步骤
        执行完常规钩子函数 返回的report报告有个属性叫report.when
        先习性when = 'setup' 返回setup的执行结果
        然后执行when = ‘call' 返回call的执行结果
        然后执行when = 'teardown' 返回 teardown的执行结果
    :return:
    """
    print("------------------------")

    # 获取钩子方法的调用结果
    out = yield
    # 从钩子方法的调用结果中 获取测试报告
    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(request):
    print("setup 前置函数")

    def fin_down():
        print("teardown后置函数")

    request.addfinalizer(fin_down)

  运行结果如下:

 

 setup失败的情况

  当setup运行失败了,setup的执行结果为failed,后面的call用例 和 teardown用例就不会执行了

 

 

 此时用例状态是error,也就是用例call都还没有开始执行,就异常了

 

call失败的情况:

  如果是setup正常执行但是call失败了,此时用例执行结果是failed,但是前置和后置 都是能够执行的

 

如果是teardown执行失败了:

 

 

 只获取call的结果:

  我们在写用例的时候,如果保证setup和teardown不报错,只关注测试用例本身的运行结果,前面的petest_runtest_makereport钩子方法执行了三个,可以增加一个判断

if report.when == 'call'

 

 

Hook函数获取失败截图的写法conftest.py:

# !/usr/bin/env python
# -*-coding:utf-8 -*-
"""
# File       : conftest.py
# Time       :2021/11/20 14:35
# Author     :author Kong_hua_sheng_25304
# version    :python 3.8
# Description:
"""
from selenium import webdriver
import pytest
driver: webdriver.Chrome = None


@pytest.mark.hookwrapper
def pytest_runtest_makereport(item):
    """
    当测试失败时,自动截图,展示到html报告中
    :param item: 这里的item是测试用例
    :return:
    """
    pytest_html = item.config.pluginmanager.getplugin('html')
    outcome = yield
    report = outcome.get_result() # 获取report对象
    extra = getattr(report,'extra',[]) #获取report对象中有没有extra属性
    print("type item: ",type(item))
    print("type report: ",type(report))
    print("extra:",extra)

    if report.when == 'call' or report.when == 'setup':
        xfail = hasattr(report,'wasfail') # 判断rport对象中是否包含wasfail
        if (report.skipped and xfail) or (report.failed and not xfail):
            file_name = report.nodeid.replace("::","_")+".png" # 将report中nodeid中 :: 用下划线代替给截图进行命名
            screen_img = _capter_screenshot() # 进行截图
            if file_name:
                html = '<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:600px;height:300px;" ' \
                       'onclick="window.open(this.src)" align="right"/></div>' % screen_img
                extra.append(pytest_html.extras.html(html))
        report.extra = extra


def _capter_screenshot():
    """
    截图保存为base64,展示到html中
    :return:
    """
    return driver.get_screenshot_as_base64()


@pytest.fixture(scope='session',autouse=True)
def browser():
    global driver

    if driver is None:
        driver = webdriver.Chrome()

    return driver

  

posted @ 2021-11-20 17:56  GalaxyStar  阅读(508)  评论(0编辑  收藏  举报