Delgan / loguru

Python logging made (stupidly) simple
MIT License
19.58k stars 694 forks source link

Question: Why does pytest not recognise AssertionError when enqueue=True? #1177

Open codeananda opened 2 months ago

codeananda commented 2 months ago

In conftest.py I have redefined caplog as per loguru's docs. I have enqueue=True because some of my code uses multiprocessing.

@pytest.fixture
def caplog(caplog: LogCaptureFixture):
    """Redefine caplog to work with loguru, taken from their docs
    """
    handler_id = logger.add(
        caplog.handler,
        format="{message}",
        level=0,
        filter=lambda record: record["level"].no >= caplog.handler.level,
        enqueue=True,  # Set to 'True' if your test is spawning child processes.
    )
    yield caplog
    logger.remove(handler_id)

I have a basic test which works when enqueue=False but not when enqueue=True. It works if I copy/paste the contents of assert_no_errors_logged underneath the test. But if I call assert_no_errors_logged, pytest complains. This is not an issue if enqueue=False.

def test_errors_are_caught(caplog):
    logger.debug("debug message")
    logger.info("an info")
    logger.error("an error")
    logger.critical("a very bad thing")
    with pytest.raises(AssertionError):
        assert_no_errors_logged(caplog)

def assert_no_errors_logged(caplog):
    error_messages = [record for record in caplog.records if record.levelno >= logging.ERROR]
    num_errors = len(error_messages)
    assert num_errors == 0

Error

============================== 1 failed in 0.28s ==============================
FAILED                [100%]2024-07-15 14:38:33.327 | DEBUG    | test_compute_statistics:test_errors_are_caught:440 - debug message
2024-07-15 14:38:33.327 | INFO     | test_compute_statistics:test_errors_are_caught:441 - an info
2024-07-15 14:38:33.327 | ERROR    | test_compute_statistics:test_errors_are_caught:442 - an error
2024-07-15 14:38:33.327 | CRITICAL | test_compute_statistics:test_errors_are_caught:443 - a very bad thing

tests\unit\test_compute_statistics.py:438 (test_errors_are_caught)
caplog = <_pytest.logging.LogCaptureFixture object at 0x000001A41CBB4550>

    def test_errors_are_caught(caplog):
        logger.debug("debug message")
        logger.info("an info")
        logger.error("an error")
        logger.critical("a very bad thing")
>       with pytest.raises(AssertionError):
E       Failed: DID NOT RAISE <class 'AssertionError'>

test_compute_statistics.py:444: Failed

Process finished with exit code 1
Delgan commented 2 months ago

Have you tried to call logger.complete() before assert_no_errors_logged()?

When enqueue=True, messages are not immediately logged: they are added to a queue and processed by a background thread. I think your test is failing because when assert_no_errors_logged() is called, not all logs reached the caplog sink yet. Adding a call to logger.complete() ensures there is no pending logs remaining.

codeananda commented 2 months ago

Aha! Yes logger.complete() makes it work. Thanks

codeananda commented 2 months ago

But hmm, does this mean I always have to call logger.complete() before assert_no_errors_logged()?

Delgan commented 4 days ago

But hmm, does this mean I always have to call logger.complete() before assert_no_errors_logged()?

Maybe that works as well if logger.complete() is called at the beginning of assert_no_errors_logged()?

You can also turn off the enqueue option for such simple tests.