diff --git a/AUTHORS b/AUTHORS index 862378be9f5..8d981ae9ad5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -9,6 +9,7 @@ Ahn Ki-Wook Alexander Johnson Alexei Kozlenok Anatoly Bubenkoff +Anders Hovmöller Andras Tim Andreas Zeidler Andrzej Ostrowski diff --git a/_pytest/logging.py b/_pytest/logging.py index 9e82e801d97..27cea16678e 100644 --- a/_pytest/logging.py +++ b/_pytest/logging.py @@ -128,6 +128,13 @@ def __init__(self, item): def handler(self): return self._item.catch_log_handler + def get_handler(self, when): + """ + Get the handler for a specified state of the tests. + Valid values for the when parameter are: 'setup', 'call' and 'teardown'. + """ + return self._item.catch_log_handlers.get(when) + @property def text(self): """Returns the log text.""" @@ -287,11 +294,16 @@ def _runtest_for(self, item, when): """Implements the internals of pytest_runtest_xxx() hook.""" with catching_logs(LogCaptureHandler(), formatter=self.formatter) as log_handler: + if not hasattr(item, 'catch_log_handlers'): + item.catch_log_handlers = {} + item.catch_log_handlers[when] = log_handler item.catch_log_handler = log_handler try: yield # run test finally: del item.catch_log_handler + if when == 'teardown': + del item.catch_log_handlers if self.print_logs: # Add a captured log section to the report. diff --git a/changelog/3117.feature b/changelog/3117.feature new file mode 100644 index 00000000000..17c64123fdd --- /dev/null +++ b/changelog/3117.feature @@ -0,0 +1 @@ +New ``caplog.get_handler(when)`` method which provides access to the underlying ``Handler`` class used to capture logging during each testing stage, allowing users to obtain the captured records during ``"setup"`` and ``"teardown"`` stages. diff --git a/doc/en/logging.rst b/doc/en/logging.rst index e3bf5603887..9a6df84844f 100644 --- a/doc/en/logging.rst +++ b/doc/en/logging.rst @@ -190,3 +190,12 @@ option names are: * ``log_file_level`` * ``log_file_format`` * ``log_file_date_format`` + +Accessing logs from other test stages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``caplop.records`` fixture contains records from the current stage only. So +inside the setup phase it contains only setup logs, same with the call and +teardown phases. To access logs from other stages you can use +``caplog.get_handler('setup').records``. Valid stages are ``setup``, ``call`` +and ``teardown``. diff --git a/testing/logging/test_fixture.py b/testing/logging/test_fixture.py index c27b31137ff..1357dcf361b 100644 --- a/testing/logging/test_fixture.py +++ b/testing/logging/test_fixture.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import logging +import pytest logger = logging.getLogger(__name__) sublogger = logging.getLogger(__name__ + '.baz') @@ -68,3 +69,23 @@ def test_clear(caplog): assert len(caplog.records) caplog.clear() assert not len(caplog.records) + + +@pytest.fixture +def logging_during_setup_and_teardown(caplog): + logger.info('a_setup_log') + yield + logger.info('a_teardown_log') + assert [x.message for x in caplog.get_handler('teardown').records] == ['a_teardown_log'] + + +def test_caplog_captures_for_all_stages(caplog, logging_during_setup_and_teardown): + assert not caplog.records + assert not caplog.get_handler('call').records + logger.info('a_call_log') + assert [x.message for x in caplog.get_handler('call').records] == ['a_call_log'] + + assert [x.message for x in caplog.get_handler('setup').records] == ['a_setup_log'] + + # This reachers into private API, don't use this type of thing in real tests! + assert set(caplog._item.catch_log_handlers.keys()) == {'setup', 'call'}