【Python-pytest】 pytest-testreport 生成测试报告

pytest-testreport 安装及使用

pytest-testreport: pytest生成html测试报告的插件,(是基于unittestreport风格的报告扩展而来),报告中会自动收集用例执行的详细日志信息,以及相关错误和输出信息

安装

pip install pytest-testreport

 

参数

--report :指定报告文件名

--title :指定报告标题

--tester :指定报告中的测试者

--desc :指定报告中的项目描述

--template :指定报告模板样式(1 or 2)

 

报告样式一

if __name__ == "__main__":
    """ run debug """
    pytest.main([__file__,
                 "--report=_report.html",   # 指定报告文件名
                 '--title=test_report 测试报告',    # 指定报告标题
                 '--tester=Phoenixy',   # 指定报告中的测试者
                 '--desc=报告描述信息',   # 指定报告中的项目描述
                 '--template=1',    # 指定报告模板样式(1 or 2)
                 '-W', "ignore:Module already imported:pytest.PytestWarning"
                 ])

 

报告:

 

报告样式二

if __name__ == "__main__":
    """ run debug """
    pytest.main([__file__,
                 "--report=_report.html",   # 指定报告文件名
                 '--title=test_report 测试报告',    # 指定报告标题
                 '--tester=Phoenixy',   # 指定报告中的测试者
                 '--desc=报告描述信息',   # 指定报告中的项目描述
                 '--template=2',    # 指定报告模板样式(1 or 2)
                 '-W', "ignore:Module already imported:pytest.PytestWarning"
                 ])

 

报告:

 

 

优化pytest_testreport

增加参数 --reportPath=xx 指定报告生成路径

修改安装目录下pytest-testreport.py文件

 

替换文件内容

# -*- coding: utf-8 -*-
import datetime
import json
import os
import time
import pytest
from jinja2 import Environment, FileSystemLoader

test_result = {
    "title": "",
    "tester": "",
    "desc": "",
    "reportPath": "",
    "cases": {},
    'rerun': 0,
    "failed": 0,
    "passed": 0,
    "skipped": 0,
    "error": 0,
    "start_time": 0,
    "run_time": 0,
    "begin_time": "",
    "all": 0,
    "testModules": set()
}


def pytest_make_parametrize_id(config, val, argname):
    if isinstance(val, dict):
        return val.get('title') or val.get('desc')


def pytest_runtest_logreport(report):
    report.duration = '{:.6f}'.format(report.duration)
    test_result['testModules'].add(report.fileName)
    if report.when == 'call':
        test_result[report.outcome] += 1
        test_result["cases"][report.nodeid] = report
    elif report.outcome == 'failed':
        report.outcome = 'error'
        test_result['error'] += 1
        test_result["cases"][report.nodeid] = report
    elif report.outcome == 'skipped':
        test_result[report.outcome] += 1
        test_result["cases"][report.nodeid] = report


def pytest_sessionstart(session):
    start_ts = datetime.datetime.now()
    test_result["start_time"] = start_ts.timestamp()
    test_result["begin_time"] = start_ts.strftime("%Y-%m-%d %H:%M:%S")


def handle_history_data(report_dir, test_result):
    """
    处理历史数据
    :return:
    """
    try:
        with open(os.path.join(report_dir, 'history.json'), 'r', encoding='utf-8') as f:
            history = json.load(f)
    except:
        history = []
    history.append({'success': test_result['passed'],
                    'all': test_result['all'],
                    'fail': test_result['failed'],
                    'skip': test_result['skipped'],
                    'error': test_result['error'],
                    'runtime': test_result['run_time'],
                    'begin_time': test_result['begin_time'],
                    'pass_rate': test_result['pass_rate'],
                    })

    with open(os.path.join(report_dir, 'history.json'), 'w', encoding='utf-8') as f:
        json.dump(history, f, ensure_ascii=True)
    return history


def pytest_sessionfinish(session):
    """在整个测试运行完成之后调用的钩子函数,可以在此处生成测试报告"""
    report2 = session.config.getoption('--report')
    reportPath = session.config.getoption('--reportPath')

    if report2:
        test_result['reportPath'] = reportPath or "reports"
        test_result['title'] = session.config.getoption('--title') or '测试报告'
        test_result['tester'] = session.config.getoption('--tester') or '小测试'
        test_result['desc'] = session.config.getoption('--desc') or ''
        templates_name = session.config.getoption('--template') or '1'
        name = report2
    else:
        return

    if not name.endswith('.html'):
        file_name = time.strftime("%Y-%m-%d_%H_%M_%S") + name + '.html'
    else:
        file_name = time.strftime("%Y-%m-%d_%H_%M_%S") + name

    if os.path.isdir(test_result['reportPath']):
        pass
    else:
        os.mkdir(test_result['reportPath'])
    file_name = os.path.join(test_result['reportPath'], file_name)
    test_result["run_time"] = '{:.6f} S'.format(time.time() - test_result["start_time"])
    test_result['all'] = len(test_result['cases'])
    if test_result['all'] != 0:
        test_result['pass_rate'] = '{:.2f}'.format(test_result['passed'] / test_result['all'] * 100)
    else:
        test_result['pass_rate'] = 0
    # 保存历史数据
    test_result['history'] = handle_history_data(test_result['reportPath'], test_result)
    # 渲染报告
    template_path = os.path.join(os.path.dirname(__file__), './templates')
    env = Environment(loader=FileSystemLoader(template_path))

    if templates_name == '2':
        template = env.get_template('templates2.html')
    else:
        template = env.get_template('templates.html')
    report = template.render(test_result)
    # print(file_name)
    with open(file_name, 'wb') as f:
        f.write(report.encode('utf8'))


@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    outcome = yield
    report = outcome.get_result()
    fixture_extras = getattr(item.config, "extras", [])
    plugin_extras = getattr(report, "extra", [])
    report.extra = fixture_extras + plugin_extras
    report.fileName = item.location[0]
    if hasattr(item, 'callspec'):
        report.desc = item.callspec.id or item._obj.__doc__
    else:
        report.desc = item._obj.__doc__
    report.method = item.location[2].split('[')[0]


def pytest_addoption(parser):
    group = parser.getgroup("testreport")
    group.addoption(
        "--reportPath",
        action="store",
        metavar="path",
        default=None,
        help="create html report file at given path.",
    )
    group.addoption(
        "--report",
        action="store",
        metavar="path",
        default=None,
        help="create html report file at given path.",
    )
    group.addoption(
        "--title",
        action="store",
        metavar="path",
        default=None,
        help="pytest-testreport Generate a title of the repor",
    )
    group.addoption(
        "--tester",
        action="store",
        metavar="path",
        default=None,
        help="pytest-testreport Generate a tester of the report",
    )
    group.addoption(
        "--desc",
        action="store",
        metavar="path",
        default=None,
        help="pytest-testreport Generate a description of the report",
    )
    group.addoption(
        "--template",
        action="store",
        metavar="path",
        default=None,
        help="pytest-testreport Generate a template of the report",
    )
pytest_testreport.py

 

 示例:

Tips: "--reportPath=../reports" 使用此参数时需要 替换依赖包中的pytest-testreport.py文件(即上面增加参数的步骤),否则默认在启动文件统计目录创建report文件夹
if __name__ == "__main__":
    """ run debug """
    pytest.main([__file__, 
                 "--reportPath=../reports",     # 指定报告生成路径 Tips:当前文件的上一级目录同级目录reports文件夹下
                 "--report=_report.html",   # 指定报告文件名
                 '--title=test_report 测试报告',    # 指定报告标题
                 '--tester=Phoenixy',   # 指定报告中的测试者
                 '--desc=报告描述信息',   # 指定报告中的项目描述
                 '--template=2',    # 指定报告模板样式(1 or 2)
                 '-W', "ignore:Module already imported:pytest.PytestWarning"
                 ])

 

结果

 

 

 

可能会与pytest-html存在冲突,若冲突需要卸载pytest-html

源码地址: https://gitee.com/lemon-test-official/pytest-testreport

posted @ 2023-04-06 10:47  Phoenixy  阅读(2155)  评论(0编辑  收藏  举报