diff --git a/AUTHORS b/AUTHORS index 3edfdcf85..81f79d609 100644 --- a/AUTHORS +++ b/AUTHORS @@ -204,6 +204,7 @@ Victor Uriarte Vidar T. Fauske Vitaly Lashmanov Vlad Dragos +Wil Cooley William Lee Wouter van Ackooy Xuan Luong diff --git a/changelog/3579.feature.rst b/changelog/3579.feature.rst new file mode 100644 index 000000000..575af006f --- /dev/null +++ b/changelog/3579.feature.rst @@ -0,0 +1 @@ +Fixture ``caplog`` now has a ``messages`` property, providing convenient access to the format-interpolated log messages without the extra data provided by the formatter/handler. diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 00bb9aeb5..615e87262 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -270,6 +270,22 @@ class LogCaptureFixture(object): """ return [(r.name, r.levelno, r.getMessage()) for r in self.records] + @property + def messages(self): + """Returns a list of format-interpolated log messages. + + Unlike 'records', which contains the format string and parameters for interpolation, log messages in this list + are all interpolated. + Unlike 'text', which contains the output from the handler, log messages in this list are unadorned with + levels, timestamps, etc, making exact comparisions more reliable. + + Note that traceback or stack info (from :func:`logging.exception` or the `exc_info` or `stack_info` arguments + to the logging functions) is not included, as this is added by the formatter in the handler. + + .. versionadded:: 3.7 + """ + return [r.getMessage() for r in self.records] + def clear(self): """Reset the list of log records and the captured log text.""" self.handler.reset() diff --git a/testing/logging/test_fixture.py b/testing/logging/test_fixture.py index 8d9ae6b51..f19813926 100644 --- a/testing/logging/test_fixture.py +++ b/testing/logging/test_fixture.py @@ -73,6 +73,27 @@ def test_log_access(caplog): assert "boo arg" in caplog.text +def test_messages(caplog): + caplog.set_level(logging.INFO) + logger.info("boo %s", "arg") + logger.info("bar %s\nbaz %s", "arg1", "arg2") + assert "boo arg" == caplog.messages[0] + assert "bar arg1\nbaz arg2" == caplog.messages[1] + assert caplog.text.count("\n") > len(caplog.messages) + assert len(caplog.text.splitlines()) > len(caplog.messages) + + try: + raise Exception("test") + except Exception: + logger.exception("oops") + + assert "oops" in caplog.text + assert "oops" in caplog.messages[-1] + # Tracebacks are stored in the record and not added until the formatter or handler. + assert "Exception" in caplog.text + assert "Exception" not in caplog.messages[-1] + + def test_record_tuples(caplog): caplog.set_level(logging.INFO) logger.info("boo %s", "arg")