Log-based testing 🪵¶
logot
makes it easy to test whether your code is logging correctly:
from logot import Logot, logged
def test_something(logot: Logot) -> None:
do_something()
logot.assert_logged(logged.info("Something was done"))
Note
logot
integrates with popular testing frameworks (e.g. pytest,
unittest). It supports many 3rd-party asynchronous and
logging frameworks, and can be extended to support many more. 💪
Why test logging? 🤔¶
Good logging ensures your code is debuggable at runtime, but why bother actually testing your logs?
Sometimes, testing logs is the only reasonable way to known your code has actually run correctly! This is particularly the case in threaded or asynchronous code.
For example, imagine the following code running in a thread:
def poll_daemon(app: App) -> None:
while not app.stopping:
sleep(app.poll_interval)
logger.debug("Poll started")
try:
app.data = app.get("http://is-everything-ok.com/")
except HTTPError:
logger.exception("Poll error")
else:
logger.debug("Poll finished")
Note
While it’s possible to rewrite this code in a way that can be tested without logot
, that risks making the
code less clear or more verbose. For complex threaded or asynchronous code, this can quickly become burdensome. 👎
Testing this code with logot
is easy!
from logot import Logot, logged
def test_poll_daemon(logot: Logot) -> None:
app.start_poll()
for _ in range(3):
logot.wait_for(logged.info("Poll started"))
logot.wait_for(logged.info("Poll finished"))
Testing threaded code¶
Use Logot.wait_for()
to pause your test until the expected logs arrive or the timeout
expires:
from logot import Logot, logged
def test_app(logot: Logot) -> None:
app.start()
logot.wait_for(logged.info("App started"))
Note
Use the timeout
argument to Logot.wait_for()
to configure how long to wait before the test fails. This can
be configured globally with the timeout
argument to Logot
, defaulting to Logot.DEFAULT_TIMEOUT
.
See also
See Log pattern matching for examples of how to wait for logs that may arrive in an unpredictable order.
Testing asynchronous code¶
Use Logot.await_for()
to pause your test until the expected logs arrive or the timeout
expires:
from logot import Logot, logged
async def test_app(logot: Logot) -> None:
asyncio.create_task(app.start())
await logot.await_for(logged.info("App started"))
Note
Use the timeout
argument to Logot.await_for()
to configure how long to wait before the test fails. This can
be configured globally with the timeout
argument to Logot
, defaulting to Logot.DEFAULT_TIMEOUT
.
See also
See Log pattern matching for examples of how to wait for logs that may arrive in an unpredictable order.
See Asynchronous frameworks for other supported asynchronous frameworks.
Testing synchronous code¶
Use Logot.assert_logged()
to fail immediately if the expected logs have not arrived:
from logot import Logot, logged
def test_something(logot: Logot) -> None:
do_something()
logot.assert_logged(logged.info("Something was done"))
Note
You can also use Logot.wait_for()
to test for expected logs, but since this only fails after a timeout
,
using Logot.assert_logged()
will give more immediate feedback if your test fails.
See also
Use Logot.assert_not_logged()
to fail immediately if the expected logs do arrive.
Further reading¶
Learn more about logot
with the following guides: