Skip to content
GitHub
Test Failures

Module-level DB pool creation blocks all unit tests

Module-level DB pool creation blocks all unit tests

Severity: P1 (broad test-suite breakage at collection time) · Applies to: any module that constructs a connection pool or other DB-dependent resource at import time Related: ADR-041 — Connection pooling · Connection pooling runbook · ADR-045 — Test Supabase instance lifecycle

Problem

Unit tests that don’t need a database fail at import time because connection.py creates a PostgreSQL connection pool as a module-level side effect. Any module that transitively imports from connection.py triggers the pool creation, which crashes if no database is available.

Symptoms:

  • psycopg2.OperationalError: connection to server at "localhost" ... refused
  • Tests fail during collection, not during execution
  • Tests for pure domain logic (no DB needed) fail because they import modules that import modules that import connection.py
  • Setting DATABASE_URL “fixes” it, but couples unit tests to infrastructure

Root Cause

# connection.py — BEFORE (module-level side effect)
_pool = psycopg2.pool.ThreadedConnectionPool(
minconn=2, maxconn=20, dsn=DATABASE_URL
)

This line executes at import time. Python’s import system is eager — when any module in the import chain touches connection.py, the pool tries to connect immediately. In a Clean Architecture codebase, the import chain is deep:

test_surgical.py
→ optimization/surgical.py
→ evaluation/evaluator.py
→ infrastructure/persistence/connection.py ← BOOM

Solution

Lazy initialization — create the pool on first use, not at import time.

# connection.py — AFTER (lazy initialization)
_pool = None
def _get_pool():
"""Get or create the connection pool (lazy initialization)."""
global _pool
if _pool is None:
_pool = psycopg2.pool.ThreadedConnectionPool(
minconn=2, maxconn=20, dsn=DATABASE_URL
)
return _pool
def get_connection():
pool = _get_pool() # ← pool created here, not at import
raw_conn = pool.getconn()
...

Result: 66 → 87 tests passing. Zero tests require a live database unless they explicitly use the db fixture.

Prevention

Best Practices

  • Never create connections, pools, or clients at module level
  • Use lazy initialization or dependency injection for all infrastructure
  • The conftest.py db fixture pattern: skip tests when DATABASE_URL is not set

Warning Signs

  • Tests failing during “collecting” phase (before any test runs)
  • ImportError or OperationalError in test output pointing to infrastructure modules
  • Tests that “work on CI but not locally” (CI has a DB, local doesn’t)
# conftest.py — DB fixture that skips gracefully
@pytest.fixture
def db():
if not os.environ.get("DATABASE_URL"):
pytest.skip("DATABASE_URL not set")
from spectral.infrastructure.persistence.connection import get_db
conn = get_db()
yield conn
conn.rollback()

References

  • Commit bdee705 — lazy pool init + test suite expansion
  • Related: Clean Architecture principle — infrastructure should never leak into domain/application imports