Как определить, прошел или не прошел тест, изучив объект Item, переданный в pytest runtest teardown?

Pytest позволяет подключаться к фазе демонтажа для каждого теста, реализуя функцию под названием pytest_runtest_teardown в плагине:

def pytest_runtest_teardown(item, nextitem):
    pass

есть ли атрибут или метод на item что я могу использовать, чтобы определить, прошел ли тест, который только что закончился, или не прошел? Я не смог найти никакой документации для pytest.Item и охота через исходный код и в ipdb не показал ничего очевидного.

2 ответов


вы также можете рассмотреть вызов.excinfo в 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.')

объект вызова содержит целую кучу дополнительной информации (время начала тестирования, время остановки и т. д.).

см.: 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 класс не имеют никакой информации о состоянии последнего теста, однако у нас есть статус общего количества неудачных тестов (в item.session.testsfailed), и мы можем использовать это:

  1. мы можем добавить нового члена item.session объект (не так хорошо, но вы должны любить python!). Этот участник сохранит статус последнего testsfailed - item.session.last_testsfailed_status.
  2. если 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