loguru: Pytest's caplog fixture doesn't seem to work
Summary
Pytest’s caplog fixture is a critical part of testing. I’d love to move to loguru, but loguru doesn’t seem to work with caplog.
I’m not sure if this is user error (perhaps it’s documented somewhere? I haven’t been able to find it.), if it is some design oversight/choice, or if the problem is actually on pytest’s end.
Expected Result
Users should be able to use loguru as a drop-in replacement for the stdlib logging package and have tests that use the caplog fixture still work.
Actual Result
Drop-in replacement causes tests that use the caplog pytest fixture to fail.
Steps to Reproduce
Base test file
# test_demo.py
import pytest
import logging
logger = logging.getLogger()
logger.addHandler(logging.StreamHandler())
# from loguru import logger
def some_func(a, b):
if a < 1:
logger.warning("Oh no!")
return a + b
def test_some_func_logs_warning(caplog):
assert some_func(-1, 2) == 1
assert "Oh no!" in caplog.text
if __name__ == "__main__":
some_func(-1, 1)
print("end")
Without Loguru:
$ python test_demo.py
Oh no!
end
(.venv) Previous Dir: /home/dthor
09:59:56 dthor@Thorium /home/dthor/temp/loguru
$ pytest
========================== test session starts ==========================
platform linux -- Python 3.6.7, pytest-4.3.0, py-1.8.0, pluggy-0.8.1
rootdir: /home/dthor/temp/loguru, inifile:
collected 1 item
test_demo.py . [100%]
======================= 1 passed in 0.03 seconds ========================
With Loguru:
Adjust test_demo.py by commenting out stdlib logging and uncommenting loguru:
...
# import logging
# logger = logging.getLogger()
# logger.addHandler(logging.StreamHandler())
from loguru import logger
...
$ python test_demo.py
2019-02-22 10:02:35.551 | WARNING | __main__:some_func:9 - Oh no!
end
(.venv) Previous Dir: /home/dthor
10:02:35 dthor@Thorium /home/dthor/temp/loguru
$ pytest
========================== test session starts ==========================
platform linux -- Python 3.6.7, pytest-4.3.0, py-1.8.0, pluggy-0.8.1
rootdir: /home/dthor/temp/loguru, inifile:
collected 1 item
test_demo.py F [100%]
=============================== FAILURES ================================
______________________ test_some_func_logs_warning ______________________
caplog = <_pytest.logging.LogCaptureFixture object at 0x7f8e8b620438>
def test_some_func_logs_warning(caplog):
assert some_func(-1, 2) == 1
> assert "Oh no!" in caplog.text
E AssertionError: assert 'Oh no!' in ''
E + where '' = <_pytest.logging.LogCaptureFixture object at 0x7f8e8b620438>.text
test_demo.py:14: AssertionError
------------------------- Captured stderr call --------------------------
2019-02-22 10:02:37.708 | WARNING | test_demo:some_func:9 - Oh no!
======================= 1 failed in 0.20 seconds ========================
Version information
$ python --version
Python 3.6.7
(.venv) Previous Dir: /home/dthor
10:10:03 dthor@Thorium /home/dthor/temp/loguru
$ pip list
Package Version
---------------------- -----------
ansimarkup 1.4.0
atomicwrites 1.3.0
attrs 18.2.0
better-exceptions-fork 0.2.1.post6
colorama 0.4.1
loguru 0.2.5
more-itertools 6.0.0
pip 19.0.3
pkg-resources 0.0.0
pluggy 0.8.1
py 1.8.0
Pygments 2.3.1
pytest 4.3.0
setuptools 40.8.0
six 1.12.0
(.venv) Previous Dir: /home/dthor
10:10:07 dthor@Thorium /home/dthor/temp/loguru
$ uname -a
Linux Thorium 4.4.0-17763-Microsoft #253-Microsoft Mon Dec 31 17:49:00 PST 2018 x86_64 x86_64 x86_64 GNU/Linux
(.venv) Previous Dir: /home/dthor
10:11:33 dthor@Thorium /home/dthor/temp/loguru
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.04.2 LTS
Release: 18.04
Codename: bionic
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 1
- Comments: 31 (19 by maintainers)
Please, note the documentation has been updated. The recommended way to integrate Loguru with
pytestis to override thecaplogfixture this way:Using
PropagateHandlercould result in dead-lock if inadvertently combined withInterceptHandlersee #474.Thank you all for code samples, they’re incredibly useful. I just want to share my own, in case any of you find it useful:
Well, this is actually not stated explicitly anywhere as far as I know. Good catch, I should add a word about this.
Would fit pretty well in the documentation page about migrating from
loggingtologuruI think.Do you think using the sample in the Readme would work for your tests?
@Delgan Thank you so much! Your snippet helped me avoid deadlock!
Looks like adding this to
conftest.pyworks:Technically you don’t even need to add
from _pytest.logging import caplog as _caplogand can just:but I really don’t like that naming collision.
I’ll write up some docs for it come Monday or Tuesday and submit the PR. Discussion can continue there.
It sounds like you’re just interested in having pytest capture log output on failures. Is that correct? If so, none of this
PropogateHandlermumbo jumbo needs to be done - pytest will already capture loguru output for tests. The “Captured stderr call” section might not be formatted the same way, but I don’t know if that matters to you.The core of this issue is specific to pytest’s
caplogfixture, which you only need if you want to assert what’s being logged. Meaning, you need thePropogateHandlerif you want to do this:@glass-ships Please open a new issue with fully reproducible example if possible.
Hi @SomeAkk.
The
catch()decorator does not propagate exceptions by default. It will simply create a logging record and send it to the handlers as any other logged message. You need to specifyreraise=Trueif you want to be able to explicitly catch it withtry / exceptor withpytest.raises().However, a little hack is possible to achieve what you want. That is, having a behavior similar to
reraise=Falsein production but being able to switch toreraise=Trueduring testing. Since the message is sent to each configured handler, you can add anerror_handler()sink that will be in charge of re-raising the error. Be careful, it must also be added with the parametercatch=Falseparameter because Loguru prevents otherwise the propagation of the error.Hi @blueyed.
Thanks for your proposition. However, I don’t wish for Loguru to expose such plugin, the code snippet in the documentation is sufficient enough as a workaround.
That’s great, thanks @dougthor42!
I think you should maybe
remove()the added sink at the end of each test. Based on your snippet, I’m wondering if this is not addind a new sink each time you run a test.