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
assertdirectly (noself.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 failRun: 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 + Assertconftest.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_valueRunning 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 outputArrange-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_*.pyor*_test.py; functions namedtest_* - 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.pyat each directory level to share fixtures scoped to that directory
See Also
- Python Essentials topic