class TestLoggingUtils
Unit test class for testing logging utilities including InvoiceExtractionLogger, PerformanceLogger, and get_logger function.
/tf/active/vicechatdev/invoice_extraction/tests/test_utils.py
297 - 470
moderate
Purpose
This test class validates the functionality of logging utilities used in an invoice extraction system. It tests logger initialization, configuration from environment variables, file logging, JSON formatted logging, request ID correlation, performance logging, and logger retrieval. Each test method sets up a clean logging environment, executes specific logging scenarios, and verifies expected behavior through assertions.
Source Code
class TestLoggingUtils(unittest.TestCase):
"""Test cases for the logging utilities."""
def setUp(self):
"""Set up test environment before each test."""
# Reset the logging configuration before each test
logging.root.handlers = []
logging.root.setLevel(logging.INFO)
def tearDown(self):
"""Clean up after each test."""
# Reset logger after each test
logging.disable(logging.NOTSET)
logging.root.handlers = []
def test_invoice_extraction_logger_init(self):
"""Test initialization of InvoiceExtractionLogger."""
config = {
'log_level': 'DEBUG',
'log_to_file': False
}
logger = InvoiceExtractionLogger(config)
# Check log level
self.assertEqual(logger.log_level, logging.DEBUG)
self.assertFalse(logger.log_to_file)
# Check that root logger was configured
self.assertEqual(logging.getLogger().level, logging.DEBUG)
self.assertEqual(len(logging.getLogger().handlers), 1)
@patch.dict(os.environ, {'INVOICE_EXTRACTION_LOG_LEVEL': 'ERROR'})
def test_log_level_from_env(self):
"""Test getting log level from environment variables."""
config = {
'log_level': 'DEBUG', # This should be overridden by env var
'log_to_file': False
}
logger = InvoiceExtractionLogger(config)
# Log level should be from environment variable
self.assertEqual(logger.log_level, logging.ERROR)
self.assertEqual(logging.getLogger().level, logging.ERROR)
def test_file_logging(self):
"""Test logging to file."""
# Use a temporary directory for log files
with tempfile.TemporaryDirectory() as temp_dir:
config = {
'log_level': 'INFO',
'log_to_file': True,
'log_dir': temp_dir,
'log_file': 'test_log.log'
}
logger = InvoiceExtractionLogger(config)
# Log a test message
logging.info("Test log message")
# Check if file exists and contains the message
log_file_path = os.path.join(temp_dir, 'test_log.log')
self.assertTrue(os.path.exists(log_file_path))
with open(log_file_path, 'r') as f:
log_content = f.read()
self.assertIn("Test log message", log_content)
def test_json_logging(self):
"""Test JSON formatted logs."""
# Capture stdout to check JSON format
captured_output = io.StringIO()
sys.stdout = captured_output
try:
config = {
'log_level': 'INFO',
'log_to_file': False,
'json_logs': True
}
logger = InvoiceExtractionLogger(config)
# Log a test message
logging.info("Test JSON log")
# Get output
log_output = captured_output.getvalue()
# Try to parse as JSON
try:
log_data = json.loads(log_output.strip())
self.assertEqual(log_data['level'], 'INFO')
self.assertEqual(log_data['message'], 'Test JSON log')
except json.JSONDecodeError:
self.fail("Log output was not valid JSON")
finally:
sys.stdout = sys.__stdout__ # Restore stdout
def test_request_id_correlation(self):
"""Test request ID correlation in logs."""
# Capture stdout to check for correlation ID
captured_output = io.StringIO()
sys.stdout = captured_output
try:
config = {
'log_level': 'INFO',
'log_to_file': False,
'json_logs': True
}
logger = InvoiceExtractionLogger(config)
logger.set_request_id("test-request-123")
# Log a test message
logging.info("Test correlated log")
# Get output
log_output = captured_output.getvalue()
# Try to parse as JSON
try:
log_data = json.loads(log_output.strip())
self.assertEqual(log_data['correlation_id'], 'test-request-123')
except json.JSONDecodeError:
self.fail("Log output was not valid JSON")
finally:
sys.stdout = sys.__stdout__ # Restore stdout
def test_performance_logger(self):
"""Test PerformanceLogger for measuring execution time."""
# Reset the logging configuration
logging.root.handlers = []
# Capture log output
log_capture = io.StringIO()
handler = logging.StreamHandler(log_capture)
logging.getLogger().addHandler(handler)
logging.getLogger().setLevel(logging.INFO)
# Use PerformanceLogger with a controlled delay
with PerformanceLogger("test_operation") as perf:
# Add some custom metrics
perf.add_metric("test_count", 5)
perf.add_metric("status", "success")
# Simulate work
datetime.now() # Just to ensure some minimal time passes
# Get log output
log_output = log_capture.getvalue()
# Check that log contains performance data
self.assertIn("Performance: test_operation completed in", log_output)
self.assertIn("test_count", log_output)
self.assertIn("status", log_output)
def test_get_logger(self):
"""Test get_logger utility function."""
# Should create logging setup if none exists
logger = get_logger("test_module")
# Check logger name
self.assertEqual(logger.name, "test_module")
# Check that root logger has handlers
self.assertGreater(len(logging.getLogger().handlers), 0)
# Check that new loggers use the same configuration
another_logger = get_logger("another_module")
self.assertEqual(another_logger.level, logger.level)
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
bases |
unittest.TestCase | - |
Parameter Details
bases: Inherits from unittest.TestCase to provide testing framework functionality including setUp, tearDown, and assertion methods
Return Value
As a test class, it does not return values. Each test method performs assertions that either pass (no return) or fail (raises AssertionError). The unittest framework collects and reports test results.
Class Interface
Methods
setUp(self) -> None
Purpose: Initializes clean logging environment before each test by resetting root logger handlers and setting default log level
Returns: None
tearDown(self) -> None
Purpose: Cleans up logging configuration after each test by disabling logging and clearing handlers
Returns: None
test_invoice_extraction_logger_init(self) -> None
Purpose: Tests initialization of InvoiceExtractionLogger with configuration dictionary, verifying log level and handler setup
Returns: None - raises AssertionError if test fails
test_log_level_from_env(self) -> None
Purpose: Tests that log level from INVOICE_EXTRACTION_LOG_LEVEL environment variable overrides config dictionary setting
Returns: None - raises AssertionError if test fails
test_file_logging(self) -> None
Purpose: Tests logging to file by creating temporary directory, logging message, and verifying file content
Returns: None - raises AssertionError if test fails
test_json_logging(self) -> None
Purpose: Tests JSON formatted logging by capturing stdout and verifying output is valid JSON with expected fields
Returns: None - raises AssertionError if test fails
test_request_id_correlation(self) -> None
Purpose: Tests request ID correlation in logs by setting correlation ID and verifying it appears in JSON log output
Returns: None - raises AssertionError if test fails
test_performance_logger(self) -> None
Purpose: Tests PerformanceLogger context manager for measuring execution time and logging custom metrics
Returns: None - raises AssertionError if test fails
test_get_logger(self) -> None
Purpose: Tests get_logger utility function for creating named loggers with proper configuration
Returns: None - raises AssertionError if test fails
Dependencies
unittestloggingjsonossysiodatetimetempfilerequests
Required Imports
import unittest
from unittest.mock import patch, MagicMock, ANY
import logging
import json
import os
import sys
import io
from datetime import datetime, timedelta
import tempfile
import requests
from utils.llm_client import LLMClient
from utils.logging_utils import InvoiceExtractionLogger, PerformanceLogger, get_logger
Usage Example
import unittest
from test_logging_utils import TestLoggingUtils
# Run all tests in the class
suite = unittest.TestLoader().loadTestsFromTestCase(TestLoggingUtils)
runner = unittest.TextTestRunner(verbosity=2)
result = runner.run(suite)
# Run a specific test
suite = unittest.TestSuite()
suite.addTest(TestLoggingUtils('test_invoice_extraction_logger_init'))
runner = unittest.TextTestRunner(verbosity=2)
result = runner.run(suite)
# Run from command line
# python -m unittest test_logging_utils.TestLoggingUtils
# python -m unittest test_logging_utils.TestLoggingUtils.test_file_logging
Best Practices
- Each test method is independent and isolated through setUp and tearDown methods
- setUp resets logging configuration before each test to ensure clean state
- tearDown cleans up logging handlers and disables logging after each test
- Tests use temporary directories for file operations to avoid polluting the filesystem
- stdout is captured and restored properly in tests that check console output
- Environment variable mocking uses @patch.dict decorator for clean isolation
- Tests verify both positive cases (expected behavior) and edge cases (environment overrides)
- File logging tests use context managers (with statements) for proper resource cleanup
- JSON parsing tests include try-except blocks with explicit failure messages
- Tests should be run in isolation or as part of a test suite, not imported for production use
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
class InvoiceExtractionLogger 73.7% similar
-
class TestAUExtractor 65.1% similar
-
class TestUKExtractor 64.9% similar
-
class TestBEExtractor 63.3% similar
-
class TestAUValidator 60.5% similar