Source: Python Extras — Connected Objects

Logging

Python’s built-in logging module for recording runtime information.

import logging
 
# Create logger for this module (best practice: one per file)
logger = logging.getLogger(__name__)
 
# Basic setup (call once at app startup)
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
 
# Log at different levels
logger.debug("Debug info")
logger.info("General info")
logger.warning("Warning")
logger.error("Error occurred")
logger.critical("Critical failure")

Log to a file (using handlers)

import logging
 
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
 
handler = logging.FileHandler('app.log')
handler.setLevel(logging.DEBUG)
 
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
 
logger.addHandler(handler)

Unit Testing with pytest

Why pytest over unittest?

  • Less boilerplate — just write functions starting with test_
  • Uses built-in assert directly (no self.assert* methods needed)

Minimal example

# test_sample.py
 
def func(x):
    return x + 1
 
def test_answer():
    assert func(3) == 4   # passes
    # assert func(3) == 5 # would fail

Run: pytest

Fixtures (Dependency Injection)

import pytest
 
@pytest.fixture
def my_fixture():
    return 42              # Arrange step
 
def test_uses_fixture(my_fixture):
    assert my_fixture == 42   # Act + Assert

conftest.py — Share fixtures across test files

Place fixtures in conftest.py at the directory level where they should be shared:

tests/
├── conftest.py        ← fixtures available to all tests here
├── unit/
│   └── test_foo.py
└── integration/
    └── test_bar.py

Testing async code

import pytest
import asyncio
 
@pytest.mark.asyncio
async def test_async_function():
    result = await some_async_function()
    assert result == expected_value

Running specific tests

pytest                          # run all tests
pytest tests/unit/              # run all tests in folder
pytest tests/unit/test_foo.py   # run specific file
pytest -v                       # verbose output

Arrange-Act-Assert pattern (pytest philosophy)

  • Arrange: set up test conditions (use fixtures)
  • Act: call the function under test
  • Assert: check the result

monkeypatch — override in tests

def test_env_var(monkeypatch):
    monkeypatch.setenv("MY_VAR", "test_value")
    assert os.environ["MY_VAR"] == "test_value"
 
def test_mock_function(monkeypatch):
    monkeypatch.setattr(module, "function_name", lambda: "fake_return")

Monkeypatch changes are automatically undone after each test.

Further pytest reference

  • Test discovery: files named test_*.py or *_test.py; functions named test_*
  • Run specific test: pytest tests/unit/test_foo.py::test_my_function
  • Run with verbose: pytest -v
  • Best practice: keep test files outside application code (tests/ directory at project root)
  • Layout: use conftest.py at each directory level to share fixtures scoped to that directory

See Also