Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

python

pytest

Pytest allows you to hook into the teardown phase for each test by implementing a function called pytest_runtest_teardown in a plugin:

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? 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.

like image 751
Jimmy Avatar asked Jun 20 '14 01:06

Jimmy


2 Answers

You may also consider call.excinfo in pytest_runtest_makereport:

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

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)
like image 166
Sharad Avatar answered Sep 30 '22 05:09

Sharad


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:

  1. We can add a new member to the item.session object (not so nice, but you gotta love python!). This member will save the status of the last testsfailed - item.session.last_testsfailed_status.
  2. If testsfailed > last_testsfailed_status - the last test the run just failed.

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
like image 31
Dekel Avatar answered Sep 30 '22 04:09

Dekel