简体   繁体   English

如何通过检查传递给pytest_runtest_teardown的Item对象来确定测试是通过还是失败?

[英]How can I determine if a test passed or failed by examining the Item object passed to the pytest_runtest_teardown?

Pytest allows you to hook into the teardown phase for each test by implementing a function called pytest_runtest_teardown in a plugin: Pytest允许您通过在插件中实现名为pytest_runtest_teardown的函数来挂钩每个测试的拆解阶段:

def pytest_runtest_teardown(item, nextitem):
    pass

Is there an attribute or method on item that I can use to determine whether the test that just finished running passed or failed? 是否有一个属性或方法item ,我可以用它来确定刚刚完成的测试运行是否通过或失败? I couldn't find any documentation for pytest.Item and hunting through the source code and playing around in ipdb didn't reveal anything obvious. 我找不到pytest.Item任何文档和通过源代码搜索并在ipdbipdb并没有发现任何明显的东西。

You may also consider call.excinfo in pytest_runtest_makereport: 您也可以在pytest_runtest_makereport中考虑call.excinfo:

def pytest_runtest_makereport(item, call):
    if call.when == 'setup':
        print('Called after setup for test case is executed.')
    if call.when == 'call':
        print('Called after test case is executed.')
        print('-->{}<--'.format(call.excinfo)) 
    if call.when == 'teardown':
        print('Called after teardown for test case is executed.')

The call object contains a whole bunch of additional information (test start time, stop time, etc.). 调用对象包含一大堆附加信息(测试开始时间,停止时间等)。

Refer: http://doc.pytest.org/en/latest/_modules/_pytest/runner.html 请参阅: http//doc.pytest.org/en/latest/_modules/_pytest/runner.html

def pytest_runtest_makereport(item, call):
    when = call.when
    duration = call.stop-call.start
    keywords = dict([(x,1) for x in item.keywords])
    excinfo = call.excinfo
    sections = []
    if not call.excinfo:
        outcome = "passed"
        longrepr = None
    else:
        if not isinstance(excinfo, ExceptionInfo):
            outcome = "failed"
            longrepr = excinfo
        elif excinfo.errisinstance(pytest.skip.Exception):
            outcome = "skipped"
            r = excinfo._getreprcrash()
            longrepr = (str(r.path), r.lineno, r.message)
        else:
            outcome = "failed"
            if call.when == "call":
                longrepr = item.repr_failure(excinfo)
            else: # exception in setup or teardown
                longrepr = item._repr_failure_py(excinfo,
                                            style=item.config.option.tbstyle)
    for rwhen, key, content in item._report_sections:
        sections.append(("Captured %s %s" %(key, rwhen), content))
    return TestReport(item.nodeid, item.location,
                      keywords, outcome, longrepr, when,
                      sections, duration)

The Node class don't have any information regarding the status of the last test, however we do have the status of the total number of failed tests (in item.session.testsfailed ), and we can use it: Node没有关于上一次测试状态的任何信息,但是我们确实具有失败测试总数的状态(在item.session.testsfailed ),我们可以使用它:

  1. We can add a new member to the item.session object (not so nice, but you gotta love python!). 我们可以在item.session对象中添加一个新成员(不太好,但你必须爱python!)。 This member will save the status of the last testsfailed - item.session.last_testsfailed_status . 该成员将保存最后一次testsfailed item.session.last_testsfailed_status的状态 - item.session.last_testsfailed_status
  2. If testsfailed > last_testsfailed_status - the last test the run just failed. 如果testsfailed > last_testsfailed_status - 最后一次测试运行失败了。

import pytest
import logging

logging.basicConfig(
    level='INFO',
    handlers=(
        logging.StreamHandler(),
        logging.FileHandler('log.txt')
    )
)

@pytest.mark.hookwrapper
def pytest_runtest_teardown(item, nextitem):
    outcome = yield

    if not hasattr(item.session, 'last_testsfailed_status'):
        item.session.last_testsfailed_status = 0

    if item.session.testsfailed and item.session.testsfailed > item.session.last_testsfailed_status:
        logging.info('Last test failed')

    item.session.last_testsfailed_status = item.session.testsfailed

Initially, I was also struggling to get the Test Status, so that I can use it to make a custom report. 最初,我也在努力获得测试状态,以便我可以使用它来制作自定义报告。
But, after further analysis of pytest_runtest_makereport hook function, I was able to see various attributes of 3 params (item, call, and report). 但是,在进一步分析pytest_runtest_makereport钩子函数后,我能够看到3个参数(项目,调用和报告)的各种属性。 Let me just list some of it out: 让我列出一些:

  1. Call: 呼叫:
    • excinfo (this further drills down to carry traceback if any) excinfo(如果有的话,进一步向下钻取跟踪)
    • start (start time of the test in float value since epoch time) start(自纪元时间起以浮点值开始测试的开始时间)
    • stop (stop time of the test in float value since epoch time) 停止(自纪元时间起停止测试的浮动时间)
    • when (can take values - setup, call, teardown) 什么时候(可以取值 - 设置,调用,拆解)
  2. item: 项目:
    • _fixtureinfo (contains info abt any fixtures you have used) _fixtureinfo(包含您使用过的任何灯具的信息)
    • nodeid (the test_name assumed by pytest) nodeid(pytest假定test_name)
    • cls (contains the class info of test, by info I mean the variables which were declared and accessed in the class of test) cls(包含测试的类信息,通过信息我的意思是在测试类中声明和访问的变量)
    • funcargs (what parameters you have passed to your test along with its values) funcargs(您传递给测试的参数及其值)
  3. report: 报告:
    • outcome (this carries the test status) 结果(这带有测试状态)
    • longrepr (contains the failure info including the traceback) longrepr(包含故障信息,包括回溯)
    • when (can take values - setup, call, teardown. please note that depending on its value the report will carry the values) 何时(可以采取值 - 设置,调用,拆解。请注意,根据其值,报告将带有值)

FYI: there are other attributes for all the above 3 params, I have mentioned in few. 仅供参考:以上3种方式都有其他属性,我已经提到了很少。 Below is the code snipped depicting, of how I have hooked the function and used. 下面是代码剪辑描述,我如何钩住函数和使用。

def pytest_runtest_makereport(item, call, __multicall__):
report = __multicall__.execute()

if (call.when == "call") and hasattr(item, '_failed_expect'):
    report.outcome = "failed"
    summary = 'Failed Expectations:%s' % len(item._failed_expect)
    item._failed_expect.append(summary)
    report.longrepr = str(report.longrepr) + '\n' + ('\n'.join(item._failed_expect))

if call.when == "call":
    ExTest.name = item.nodeid
    func_args = item.funcargs
    ExTest.parameters_used = dict((k, v) for k, v in func_args.items() if v and not hasattr(v, '__dict__'))
    # [(k, v) for k, v in func_args.items() if v and not hasattr(v, '__dict__')]
    t = datetime.fromtimestamp(call.start)
    ExTest.start_timestamp = t.strftime('%Y-%m-%d::%I:%M:%S %p')
    ExTest.test_status = report.outcome
    # TODO Get traceback info (call.excinfo.traceback)  
    return report

Hook wrappers are the way to go - allow all the default hooks to run & then look at their results. Hook包装器是可行的方法 - 允许所有默认挂钩运行,然后查看其结果。

Below example shows 2 methods for detecting whether a test has failed (add it to your conftest.py ) 下面的示例显示了检测测试是否失败的2种方法(将其添加到conftest.py

@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
    # Because this is a hookwrapper, calling `yield` lets the actual hooks run & returns a `_Result`
    result = yield
    # Get the actual `TestReport` which the hook(s) returned, having done the hard work for you
    report = result.get_result()

    # Method 1: `report.longrepr` is either None or a failure representation
    if report.longrepr:
        logging.error('FAILED: %s', report.longrepr)
    else:
        logging.info('Did not fail...')

    # Method 2: `report.outcome` is always one of ['passed', 'failed', 'skipped'] 
    if report.outcome == 'failed':
        logging.error('FAILED: %s', report.longrepr)
    elif report.outcome == 'skipped':
        logging.info('Skipped')
    else:  # report.outcome == 'passed'
        logging.info('Passed')

See TestReport documentation for details of longrepr and outcome 有关longreproutcome详细信息,请参阅TestReport文档

(It doesn't use pytest_runtest_teardown as the OP requested but it does easily let you check for failure) (它不使用pytest_runtest_teardown作为请求的OP,但它很容易让你检查失败)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM